Check what your app needs
App auto-approval works with production credentials, for any company’s app that has aclient_id and a client_secret. Your app gets its platform access token with client credentials, and the examples on this page send that token as $ORG_TOKEN. Every purchase your app pays this way charges a real card.
A public OAuth app has no secret, so it can’t ask for app auto-approval. Test mode can’t either: the dashboard shows the option as unavailable until you switch it to Live, and the API refuses the link under sandbox credentials. A purchase in test mode always waits for the user’s approval.
Your checkout code also has to tell Agentcard which page each purchase comes from. The checkout SDK, @agent-cards/checkout, does this for you from version 0.7.0, as Send the checkout origin explains.
Set a default in the dashboard
Set a default once, and every vault session your app creates from then on asks for app auto-approval. Only an owner or admin of your company can change the default. The dashboard’s labels call the user the customer.- Open the dashboard and switch it to Live.
- Go to Settings → Vault → Customize.
- Under Card enrollment, pick your app.
- Choose
Request app auto-approval. - Check “Our app will obtain customer approval before each purchase. Agentcard does not verify that approval.”
- Select
Save enrollment default.
url.
To stop asking, choose Customer chooses and save. New links stop asking, and permissions users already granted stay on.
Ask on one link
To ask on one link, or to override your app’s default, sendapproval_mode when you create the vault session:
approval_mode takes partner to ask for app auto-approval, or customer to leave the choice to the user, as on any other link. Without it, the link follows your app’s default from the dashboard. Send url to the user wherever you already talk to them.
expires_in sets how long the link lives, from 60 seconds to 48 hours, and defaults to 24 hours. Add user_id to send the link to a user already connected to your app: when they open it, Agentcard sends them a one-time code instead of a new account setup.
Under sandbox credentials, Agentcard refuses the request and creates no link:
approval_mode: "customer", or set your app’s default back to Customer chooses.
See what the user approves
The link opens the Vault. A new user adds a card and sets up a passkey. A returning user signs in with their passkey and picks one of their cards. On Add your card, the Vault says what the user allows:Learn when the card is ready
Receive these events at your webhook endpoint, or poll the session.
A link created with
user_id never fires vault.session_linked, because you already hold the id.
vault.payment_permission.updated
grant_id names the permission on that card, and allowance_id is the same for every card the user gives your app. The same event reports every later change: revoked when the user turns the permission off or your app’s connection to the user ends, and retired when the user removes the card. A delivery can repeat or arrive late, so keep the highest revision you have for each grant_id and ignore any event with a lower or equal one. The event’s page shows every field.
To poll instead, read the session every poll_interval seconds until payment_permission.ready is true:
status turns linked as soon as the user signs in or sets up their passkey, before they grant the permission, so wait for ready.
Later, List a user’s vaulted cards returns the same
payment_permission on each card.
Get the user’s tokens
A purchase needs only your platform access token, so most apps skip this step. When your app also calls endpoints that run with the user’s own connection token, exchange the linked session once:Send the checkout origin
Sendcheckout_origin, the origin of the top-level checkout page such as https://shop.example.com, with every checkout authorization. Without it, the purchase waits for the user: Agentcard pays without asking only when it knows which page the purchase comes from.
The checkout SDK sends it for you from version 0.7.0. When the merchant’s card request pauses, the SDK reads the origin of the top-level page, never the payment processor’s frame. An older version never sends it, so none of its purchases go through without the user. Install the current release:
checkout_origin to each checkout authorization you create:
checkout_origin as the exact origin: https://, the host in lower case, and nothing after the host except a port other than 443. A path, a trailing slash, an uppercase letter or :443 makes the value invalid, and Agentcard refuses the checkout authorization:
checkout_origin still goes through, but the purchase waits for the user, and the authorization says why:
Pay without an approval
Create the checkout authorization with your platform access token, as in Completing a purchase, and pass the storeduser_id in the user field. The checkout SDK makes this call for you. Agentcard pays without asking the user when every one of these is true:
- The purchase runs under production credentials.
- The checkout authorization carries
checkout_origin. - The purchase is in US dollars, and Agentcard knows the amount. Pass
amountandcurrencywith a purchase that turns the card into a token, because that request carries no amount. A one-step Stripe payment qualifies without them, because Agentcard reads the amount from Stripe. - Exactly one of the user’s cards carries an active permission for your app, or the checkout authorization names the card with
card_id, which the SDK takes ascardId. - The checkout sends the card in a step the table below marks as paid without asking.
The other
token processors in List recognized processors work like the first row: Agentcard completes their card request in place of the user’s device.
When the purchase qualifies, Agentcard pays with the card the user gave your app and asks nobody. The authorization carries execution_mode: "autopilot", the grant_id of the permission that paid, an autopilot_status, and no approvalUrl. Read it until it finishes:
response into the merchant request your browser paused, as after an approval. The SDK does this for you. On approved, your webhook endpoint also receives checkout_authorization.approved with execution_mode: "autopilot" and the same grant_id.
A purchase that turned the card into a token finishes with charged_kind: "none". The merchant charges that token from its own server afterwards, so Agentcard never sees the charge, and you report what the merchant did. A one-step Stripe payment finishes with charged_kind: "captured", or "authorized" when the merchant captures later, and Agentcard reads the payment back from Stripe into settlement, as after any approval.
A user can also turn on auto-approval rules of their own in the Vault, with limits they set. Those rules pay only for apps the user never gave a permission. Once the user gives your app one, only your app’s permission pays for its purchases.
Handle a purchase that waits
Any other purchase waits for the user. The authorization staysawaiting_approval with execution_mode: "user_approval" and an approvalUrl to send the user, as without app auto-approval. With the SDK, onApprovalUrl fires only for these purchases.
Follow an unfinished payment
autopilot_status follows Agentcard’s attempt to pay. Agentcard sends checkout_authorization.approved when the attempt succeeds and checkout_authorization.declined when the processor declines the card, so read the authorization to learn every other result. Branch on execution_mode as well as status: an authorization can read awaiting_approval while Agentcard is still paying, and only user_approval carries an approvalUrl.
When the user’s bank asks for 3D Secure, the payment stops at
action_required, and the user never sees the bank’s request: Agentcard can’t answer it for them, and the Vault doesn’t show it. The payment stays open until Stripe reports it finished or canceled, and the authorization stays awaiting_approval until then. The current SDK raises PaymentOutcomeUnknownError with the reason autopilot_action_required.
Report what the merchant did
When an auto-approved purchase finishes withcharged_kind: "none", report what the merchant charged. Send the report with the platform access token of the app that created the purchase:
201 with the entry, and every later read of the authorization carries outcome with the net amount you reported. Send declined instead when the merchant’s charge failed, with an optional reason.
Report each refund after the charge, one call per refund:
processor_reference. When the processor gave you none, send your own id for that refund in partner_reference, the same id on every retry. A report sent again answers 200 with replayed: true and adds nothing, so a retry is safe.
Agentcard records your report as you sent it and does not verify it. Report a checkout outcome lists every rule and error.
Ask for permission again
A permission that readsrevoked comes back from a new link. Create a vault session with approval_mode: "partner" and send it to the user. When they pick the same card on Select your card, the Vault opens the Restore Acme permission screen, and one tap on the card turns the permission back on. The permission keeps its grant_id, its revision goes up, and vault.payment_permission.updated reports active again.
A permission that reads retired doesn’t come back, because the user removed the card. Send a new link so the user adds a card, and your app gets a new permission for it.
Give your agent rules
- Get the user’s approval before each purchase. Agentcard pays without asking them and never checks that your app asked.
- Send the checkout page’s own origin as
checkout_origin, never the payment processor’s frame, such ashttps://js.stripe.com. Agentcard names the merchant from it. - Pass
amountandcurrencyon every purchase. A request that only turns the card into a token carries no amount, and without one the purchase waits for the user. - Pass
card_idonce the user has given your app more than one card. Without it, every purchase waits for the user. - Never start a second purchase for an order whose first purchase reads
outcome_unknownoraction_required. The first payment is still open, and Agentcard doesn’t know yet whether it will charge the card. - Report every charge, decline and refund of a purchase that ends with
charged_kind: "none", and give each refund its own reference, so a retry never counts twice.