REST API
A small JSON API over HTTPS.
text
https://api.openintents.io/v1Authentication
Send your API key as a Bearer token. oi_test_ keys
create test intents; oi_live_ keys make real purchases.
bash
curl https://api.openintents.io/v1/intents \
-H "Authorization: Bearer $OPENINTENTS_API_KEY"Conventions
- Amounts are integers in the smallest currency unit (cents for USD).
- Timestamps are ISO 8601 in UTC.
POSTrequests accept anIdempotency-Keyheader. Retrying with the same key returns the original intent instead of creating a second one. Use it for anything that spends money.- Lists are cursor-paginated: pass
limit(max 100) and thenext_cursorfrom the previous page.
Create an intent
POST/v1/intents
| Parameter | Type | |
|---|---|---|
intent | string | Required. What to buy or book, in natural language. |
max_amount | integer | Spending cap in cents. The intent fails rather than exceed it. |
currency | string | ISO currency code. Defaults to the wallet's currency. |
context | object | Structured details: addresses, traveler names, loyalty numbers. |
require_payment_link | boolean | Always return a payment link at checkout instead of paying from the balance. |
metadata | object | Up to 20 of your own key/value pairs. |
bash
curl https://api.openintents.io/v1/intents \
-H "Authorization: Bearer $OPENINTENTS_API_KEY" \
-H "Idempotency-Key: 5b1f6c1e-coffee-0845" \
-H "Content-Type: application/json" \
-d '{
"intent": "flat white, oat milk, pickup 8:45 at the Blue Bottle on 5th",
"max_amount": 1000,
"metadata": { "user": "u_42" }
}'ts
const res = await fetch("https://api.openintents.io/v1/intents", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENINTENTS_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({ intent: "book an Uber to SFO at 7am", max_amount: 6000 }),
});
const intent = await res.json(); // { id: "int_…", status: "queued", … }python
import os, uuid, requests
intent = requests.post(
"https://api.openintents.io/v1/intents",
headers={
"Authorization": f"Bearer {os.environ['OPENINTENTS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"intent": "reorder my usual coffee beans on Amazon", "max_amount": 3000},
).json()Returns the intent object with
"status": "queued" and HTTP 201.
Retrieve an intent
GET/v1/intents/{id}
Returns the intent. Poll every 2 to 5 seconds, or use webhooks.
List intents
GET/v1/intents?status=completed&limit=20
json
{
"object": "list",
"data": [{ "id": "int_7f3a2c", "status": "completed" }],
"next_cursor": "int_7f29aa"
}Get the balance
GET/v1/balance
json
{ "object": "balance", "available": 24450, "currency": "usd", "monthly_limit": 100000, "spent_this_month": 67321 }Intents pay from this balance when it covers the total and fee within your limits; otherwise they return a payment link.
Cancel an intent
POST/v1/intents/{id}/cancel
Cancels an intent that has not paid yet (queued, running or
requires_payment). Cancelled intents are free.
Errors
Errors use standard HTTP status codes and a consistent body:
json
{
"error": {
"type": "invalid_request",
"code": "max_amount_too_low",
"message": "max_amount must be at least 50 (cents).",
"param": "max_amount"
}
}| Status | type | When |
|---|---|---|
400 | invalid_request | Missing or malformed parameters. |
401 | authentication | Missing, invalid or revoked API key. |
402 | payment | The payment for an intent was declined. |
404 | not_found | No such intent in this account. |
409 | conflict | Action not allowed in the intent's current status. |
429 | rate_limit | Too many requests; retry after the Retry-After header. |
5xx | api_error | Our fault. Safe to retry with the same Idempotency-Key. |
Failures during an intent (sold out, over budget) are not HTTP errors: the
intent ends in failed with an error.code such as over_max_amount,
out_of_stock or merchant_unavailable.