Skip to main content
This is the whole integration, in the order you should build it. You do not need to understand the rest of the platform first — four steps, and each one is verifiable on its own before you move to the next. By the end you will be able to complete a fulfilled purchase, handle a refusal, and diagnose a delivery problem without asking us to look in our database.
Everything here is implemented in one runnable file: sdk/examples/reference-merchant/server.ts. Read that alongside this page — it is the same four steps, in the same order.

What you are building

Alyte lets a buyer’s AI agent purchase from your shop within limits the buyer set. Three facts shape every integration decision, so they are worth reading once before writing code:
  1. Your API token cannot spend. There is no merchant-side endpoint that charges a buyer. You hand off; the buyer approves in their own session. This is structural, not a permission you can be granted.
  2. Money settles to your own PSP. Alyte never holds your funds.
  3. A refused purchase is usually refused before any payment exists. That shapes how you diagnose problems — see step 4.

Step 1 · Hand off to the buyer

There is no API call here. You send the buyer to their authorize page:
That page shows which agent will buy, its remaining spend limit, and the price, then enforces every limit server-side. The buyer’s one tap is the consent moment. If the buyer is already signed in with you, mint a buyer session first so their identity carries over and they don’t sign in twice. Verify this step: open the link yourself. You should see your tier, and a button whose verb matches the tier’s state (buy now, or queue for the drop).

Step 2 · Receive the purchase

Register a webhook endpoint, then verify a delivery before a real sale depends on it:
The SDK verifies and dispatches:
Pass the raw bytes. Verification is over the exact bytes we signed, so a JSON body parser will break a valid signature. This is the single most common setup mistake.
Failure behaviour, which you can rely on: Verify this step: the test delivery reaches your handler and verifies.

Step 3 · Fulfil exactly once

Two rules. Fulfil on tierId and quantity — never by matching the amount back to a price. Prices change, and two tiers can cost the same. Own your idempotency. Deliveries are at-least-once: we retry until we see a 2xx, so your handler can run twice for one sale — most often when it succeeded but the response never reached us. The SDK deliberately does not dedupe for you, because it cannot do it safely. The dangerous window is inside your own process: issue the ticket and crash before recording “handled” and a retry double-issues; record first and crash and the buyer paid for nothing. Only your database closes that window, by writing both facts in one transaction:
Claim first, inside the transaction, then do the work. If anything throws, the whole thing rolls back and the retry tries again cleanly. Dedupe on event.eventId, which is stable across retries — never on event.id, which is the delivery id and changes every attempt. Verify this step: send the same delivery twice; you should issue one ticket.

Step 4 · Diagnose it yourself

When a buyer says “my agent didn’t buy”, start here:
You get the watch’s state, a plain-language summary, the typed refusal reason, whether the outcome is terminal, the linked payment if one happened, and deliveries — every webhook attempt with its status and error. Two things this answers that nothing else does:
  • Refusals with no payment to look up. The spend cap is checked before a payment intent is created, so spend_cap_exceeded, a quantity guardrail, a dead card, sale_ended and agent_gone all produce a failed watch and no intent id. This is the entry point that always works.
  • “Did the webhook reach me?” deliveries distinguishes we never sent it from your endpoint rejected it — check status, attempts and lastError before suspecting your own code.
A failed watch is terminal: it does not retry. That surprises people more than anything else in the system, so the response says so explicitly.
merchantFulfilment and buyerNotified always read unknown, on purpose. We record webhook delivery, never whether you issued a ticket — we won’t claim knowledge we don’t have.
If you have an intent id instead, GET /v1/integration/payments/{intentId} returns the same delivery history plus the reverse link to the watch.

You’re done

That is a complete integration. From here:
  • Lifecycle — every state a purchase can be in, and how to tell the recurring support cases apart
  • Inventory and fulfilment — stock, holds, and why oversell is impossible
  • Webhooks — retries, secret rotation, event reference
  • Errors — the typed taxonomy and which codes are retryable
  • Conversational shopfront — if a chat assistant is doing the hand-off