Browse docs
previewThese docs describe OpenIntents as it is being built. Endpoints and payloads may change before launch.

REST API

A small JSON API over HTTPS.

text
https://api.openintents.io/v1

Authentication

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.
  • POST requests accept an Idempotency-Key header. 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 the next_cursor from the previous page.

Create an intent

POST/v1/intents
ParameterType
intentstringRequired. What to buy or book, in natural language.
max_amountintegerSpending cap in cents. The intent fails rather than exceed it.
currencystringISO currency code. Defaults to the wallet's currency.
contextobjectStructured details: addresses, traveler names, loyalty numbers.
require_payment_linkbooleanAlways return a payment link at checkout instead of paying from the balance.
metadataobjectUp 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"
  }
}
StatustypeWhen
400invalid_requestMissing or malformed parameters.
401authenticationMissing, invalid or revoked API key.
402paymentThe payment for an intent was declined.
404not_foundNo such intent in this account.
409conflictAction not allowed in the intent's current status.
429rate_limitToo many requests; retry after the Retry-After header.
5xxapi_errorOur 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.