Buyer integration guide

Download the complete public OpenAPI schema

Buyer integration guide

This buyer-side walkthrough covers an end-to-end test-mode purchase: create a profile, attach a payment method tokenized through an OpenMerchant Element, create a PurchaseIntent, approve it, and confirm the purchase succeeded. The example payloads below are illustrative — copy them into your own client to try the flow against the test environment.

For merchant-side pricing and fulfillment, see the Merchant integration guide.

All requests use a test-mode secret API key in the Authorization header:

Authorization: Bearer pr_sk_test_...

1. Create a profile

A profile represents the end customer the purchase is billed and shipped to. Create one with POST /v1/profiles.

Request
POST /v1/profiles
Content-Type: application/json

{
  "profile_type": "consumer",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "email": "ada@example.com",
  "phone": "+14155551234",
  "dob": { "day": 10, "month": 12, "year": 1990 },
  "country": "US",
  "addresses": [
    {
      "address_type": "billing",
      "is_default": true,
      "recipient_name": "Ada Lovelace",
      "line1": "548 Market St",
      "line2": "Suite 200",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94104",
      "country": "US"
    }
  ]
}
Response — 201 Created
{
  "id": "pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2",
  "profile_type": "consumer",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "email": "ada@example.com",
  "country": "US",
  "status": "active",
  "addresses": [
    {
      "id": "pr_addr_01K8Q4R3F2W6S0E1N5Y7K9TM2C",
      "address_type": "billing",
      "is_default": true,
      "recipient_name": "Ada Lovelace",
      "line1": "548 Market St",
      "line2": "Suite 200",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94104",
      "country": "US"
    }
  ],
  "created_at": "2026-06-07T18:02:14Z"
}

Save the returned profile id and the address id — both are referenced by later calls.

2. Add a payment method

Card numbers and CVCs never touch your server. In your frontend, mount a OpenMerchant Element to collect the card; submitting the element returns an opaque vault_token_id that you forward to your backend. Then attach the card to the profile with POST /v1/profiles/{profile_id}/payment_methods.

Request
POST /v1/profiles/pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2/payment_methods
Content-Type: application/json

{
  "type": "card",
  "is_default": true,
  "label": "Personal Visa",
  "billing_address": {
    "id": "pr_addr_01K8Q4R3F2W6S0E1N5Y7K9TM2C"
  },
  "card": {
    "kind": "existing",
    "vault_token_id": "tok_test_VxQF8s4uYwK7d2hP",
    "cardholder_name": "Ada Lovelace",
    "agentic_credentials_enrollment_requested": true,
    "agentic_terms_acknowledged": true
  }
}
Response — 201 Created
{
  "id": "pr_ppm_01K8Q4R7TC0SJ2D1A8K9X3WZQH",
  "profile_id": "pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2",
  "type": "card",
  "is_default": true,
  "label": "Personal Visa",
  "card": {
    "kind": "existing",
    "is_issued": false,
    "card_brand": "visa",
    "card_last4": "4242",
    "card_exp_month": 12,
    "card_exp_year": 2030,
    "agentic_credentials_status": "active"
  }
}

The returned id is the profile_payment_method_id you'll reference when creating a PurchaseIntent.

3. Create a PurchaseIntent

A PurchaseIntent is the agent's intent to buy one or more items on behalf of the profile. Create one with POST /v1/purchase_intents.

Request
POST /v1/purchase_intents
Content-Type: application/json

{
  "profile_id": "pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2",
  "profile_payment_method_id": "pr_ppm_01K8Q4R7TC0SJ2D1A8K9X3WZQH",
  "currency_code": "USD",
  "items": [
    {
      "url": "https://shop.example.com/products/notebook-a5",
      "quantity": 1,
      "expected_amount": 2400,
      "expected_currency": "USD"
    }
  ],
  "preferences": {
    "fulfillment": {
      "method": "ship",
      "max_fee_amount": 1500
    }
  },
  "idempotency_key": "demo-2026-06-07-001"
}
Response — 201 Created
{
  "id": "pr_pi_01K8Q4RDX9N5G7P2H1S0YV3JR8",
  "object": "purchase_intent",
  "profile_id": "pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2",
  "profile_payment_method_id": "pr_ppm_01K8Q4R7TC0SJ2D1A8K9X3WZQH",
  "currency_code": "USD",
  "expected_amount": 2400,
  "max_allowed_amount": 4150,
  "actual_amount": 0,
  "status": "awaiting_approval",
  "items": [
    {
      "id": "pr_pii_01K8Q4RDXA1V7Z6W4M2C0BPQXJ",
      "url": "https://shop.example.com/products/notebook-a5",
      "quantity": 1
    }
  ],
  "carts": [],
  "created_at": "2026-06-07T18:02:47Z"
}

The PurchaseIntent enters awaiting_approval because the policy requires an explicit approve step before execution. carts is empty until execution begins. If your account's policy allows unattended purchases up to this amount, the status will skip straight to queued and you can move to step 5.

4. Approve the PurchaseIntent

Approve the intent with POST /v1/purchase_intents/{purchase_intent_id}/approve. This is the final go-ahead before charges are attempted.

Request
POST /v1/purchase_intents/pr_pi_01K8Q4RDX9N5G7P2H1S0YV3JR8/approve
Content-Type: application/json

{
  "approve": true,
  "profile_payment_method_id": "pr_ppm_01K8Q4R7TC0SJ2D1A8K9X3WZQH"
}
Response — 200 OK
{
  "id": "pr_pi_01K8Q4RDX9N5G7P2H1S0YV3JR8",
  "object": "purchase_intent",
  "status": "approved",
  "expected_amount": 2400,
  "max_allowed_amount": 4150,
  "actual_amount": 0,
  "carts": [],
  "updated_at": "2026-06-07T18:03:02Z"
}

The status moves to approved and the PurchaseIntent is queued for execution. carts will be populated as merchant checkout begins.

5. Retrieve the PurchaseIntent

Poll GET /v1/purchase_intents/{purchase_intent_id} until the status reaches a terminal value (succeeded, failed, cancelled, or declined).

Request
GET /v1/purchase_intents/pr_pi_01K8Q4RDX9N5G7P2H1S0YV3JR8
Response — 200 OK
{
  "id": "pr_pi_01K8Q4RDX9N5G7P2H1S0YV3JR8",
  "object": "purchase_intent",
  "status": "succeeded",
  "profile_id": "pr_pf_01K8Q4R2P1J0H4Y9N3FZP1T9V2",
  "profile_payment_method_id": "pr_ppm_01K8Q4R7TC0SJ2D1A8K9X3WZQH",
  "currency_code": "USD",
  "expected_amount": 2400,
  "max_allowed_amount": 4150,
  "actual_amount": 2675,
  "items": [
    {
      "id": "pr_pii_01K8Q4RDXA1V7Z6W4M2C0BPQXJ",
      "url": "https://shop.example.com/products/notebook-a5",
      "quantity": 1
    }
  ],
  "carts": [
    {
      "id": "pr_pic_01K8Q4RHGN8Y3M4D5L7B9TZP1W",
      "purchase_attempt_id": "pr_pia_01K8Q4RJ8M5C9B2X1V7Q3WZK4D",
      "status": "succeeded",
      "attempt_number": 1,
      "max_attempts": 3,
      "can_retry": false,
      "can_cancel": false,
      "merchant": "Shop Example",
      "merchant_domain": "shop.example.com",
      "expected_amount": 2400,
      "max_allowed_amount": 4150,
      "actual_amount": 2675,
      "currency_code": "USD",
      "order_id": "SE-2026-1284910",
      "transaction_id": "pr_txn_01K8Q4RKQ4B7T2H6F9V3J1WPLY",
      "started_at": "2026-06-07T18:03:08Z",
      "completed_at": "2026-06-07T18:03:36Z"
    }
  ],
  "created_at": "2026-06-07T18:02:47Z",
  "started_at": "2026-06-07T18:03:02Z",
  "completed_at": "2026-06-07T18:03:38Z"
}

The PurchaseIntent reaches the terminal status: "succeeded" and the cart reaches status: "succeeded". actual_amount is the charged total (item subtotal plus shipping and tax) — always less than or equal to max_allowed_amount. While a cart is still executing it will pass through created, in_progress, and possibly awaiting_merchant_auth (if the merchant needs the shopper to sign in) before reaching a terminal status (succeeded, failed, or cancelled).

Next steps

  • Subscribe to lifecycle events at GET /v1/purchase_intents/{id}/events instead of polling.
  • Cancel an in-flight intent with POST .../cancel.
  • Retry a failed intent with POST .../retry.
  • Manage spending limits and merchant restrictions via Policies.