Developer

One request. Three possible answers.

PayCarter gives an AI agent one thing to do before it buys anything: ask. It hears back approved, pending or denied, and only an approved purchase gets payment details. Connect over an HTTP API or an MCP server.

Base addresshttps://api.paycarter.com
MCP serverhttps://api.paycarter.com/mcp
Sign-inBearer token
FormatJSON

The preview

This runs on a test payment network. No money moves.The payment details it returns are a test card number. Real cards arrive once the card networks have approved PayCarter, and the shapes on this page may change before then.

Access is by invitation while PayCarter is pre-launch. A company gets three kinds of token:

TokenWho holds itWhat it can do
AgentOne AI agent, tied to one named employeeAsk to buy, collect payment details for an approved purchase, report the outcome
ApproverThe person named in the spending rulesSee what is waiting for them, approve or decline
AdminWhoever runs PayCarter for the companySet up people, rules, cards and agents, issue and revoke tokens, read every purchase

Send the token on every call as Authorization: Bearer <token>. Tokens are shown once when they are made, and only a hash is stored. A token can only ever act as what it was issued for: an agent cannot approve its own purchase.

How a purchase runs

  1. The agent asksMerchant, amount, currency and a reason, before it reaches any checkout.
  2. The rules are checkedCurrency, blocked and approved merchants, the limit per purchase, the monthly budget. The reply lists every rule and how it came out.
  3. A person approves, if neededAnything above the approval threshold waits for the named approver, who gets a link to a one-screen decision.
  4. The agent collects payment detailsFor that purchase only, and only once.
  5. The agent reports what happenedCompleted or failed, the amount charged and the order reference. All of it lands in one audit trail.

Every purchase request has a summary that is always one of three words. It is the only field an agent needs to branch on.

SummaryMeaningWhat the agent should do
approvedWithin the rules, or a person has said yesGo to the checkout, then collect payment details
pendingWaiting for the approverCheck back later. Do not send the request again
deniedA rule stopped it, the approver declined, or it lapsedDo not attempt the purchase. Say which rule stopped it

An approved or pending request holds its amount against the monthly budget, so two requests cannot both squeeze under the limit. Permission lapses if it is not used in time (24 hours unless the company sets otherwise), and a lapsed, declined or failed purchase gives its budget back.

Quick start

Four calls take a purchase from request to receipt. The examples use the demo rules: a 250.00 limit per purchase, 2,000.00 a month, and approval needed above 100.00.

1. Ask to buy

POST/v1/intents
curl https://api.paycarter.com/v1/intents \
  -H "Authorization: Bearer $PAYCARTER_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant": { "name": "Fernhill Software", "url": "https://fernhill.example", "countryCode": "GB" },
    "amount": "240.00",
    "currency": "GBP",
    "reason": "Annual plan, design team"
  }'

240.00 is above the approval threshold, so the answer is pending:

201 Created
{
  "intent": {
    "id": "int_76c3efbc6ba3406e9fcb5b9d2fdd21ff",
    "summary": "pending",
    "status": "pending",
    "merchant": { "name": "Fernhill Software", "url": "https://fernhill.example", "countryCode": "GB" },
    "amount": "240.00",
    "currency": "GBP",
    "reason": "Annual plan, design team",
    "checks": [
      { "rule": "currency", "passed": true, "detail": "Purchase is in GBP." },
      { "rule": "blocked_merchant", "passed": true, "detail": "Fernhill Software is not blocked." },
      { "rule": "per_purchase_limit", "passed": true, "detail": "Within the 250.00 GBP limit per purchase." },
      { "rule": "monthly_budget", "passed": true, "detail": "1760.00 GBP of the 2000.00 GBP monthly budget left after this purchase." },
      { "rule": "approval_threshold", "passed": false, "detail": "Above 100.00 GBP, so the approver decides." }
    ],
    "flags": [],
    "createdAt": "2026-10-12T14:09:20.711Z",
    "expiresAt": "2026-10-13T14:09:20.711Z",
    "events": [ /* the audit trail so far */ ]
  }
}

2. The approver decides

Usually on the approval screen, from a link. With the approver's token it is one call:

POST/v1/intents/{id}/decision
curl https://api.paycarter.com/v1/intents/$INTENT_ID/decision \
  -H "Authorization: Bearer $PAYCARTER_APPROVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "approve", "reason": "Renewal agreed in the budget" }'

3. Collect payment details

POST/v1/intents/{id}/credential
curl -X POST https://api.paycarter.com/v1/intents/$INTENT_ID/credential \
  -H "Authorization: Bearer $PAYCARTER_AGENT_TOKEN"
200 OK
{
  "intent": { "id": "int_76c3…", "summary": "approved", "status": "credential_issued" /* … */ },
  "credential": {
    "cardNumber": "4111111111111111",
    "expirationDate": "12/2030",
    "securityCode": "123"
  }
}

This works once per purchase. A second call answers 409 credential_already_issued. PayCarter does not store or log these details.

4. Report what happened

POST/v1/intents/{id}/confirmation
curl https://api.paycarter.com/v1/intents/$INTENT_ID/confirmation \
  -H "Authorization: Bearer $PAYCARTER_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "completed", "amount": "240.00", "orderId": "FH-10042" }'

If the amount reported is more than was approved, the purchase is still recorded and is flagged amount_over_approved for the company to see.

Connect an agent with MCP

The same flow is available as tools over the Model Context Protocol, so an agent that speaks MCP needs no custom code.

Server addresshttps://api.paycarter.com/mcp
TransportStreamable HTTP
HeaderAuthorization: Bearer <agent token>

Many MCP clients take a block like this. The key names vary, so check your client's own documentation.

Example client settings
{
  "mcpServers": {
    "paycarter": {
      "type": "http",
      "url": "https://api.paycarter.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_AGENT_TOKEN" }
    }
  }
}

The agent is identified by the token on the connection, never by anything it passes to a tool, so one agent cannot act as another. Approver and admin tokens are refused.

Tools

ToolTakesGives back
request_purchasemerchant_name, amount, currency, reason. Optional: merchant_url, merchant_country, merchant_category_codeThe request, its summary, each rule checked, and a next_step written for the agent
get_purchaseintent_idThe current state, for example whether a pending request has been approved
get_payment_credentialintent_idCard number, expiry and security code for that purchase. Works once
confirm_purchaseintent_id, status (completed or failed). Optional: amount, order_idThe finished record
request_purchase, pending
{
  "intent_id": "int_662cca55acf7451a9c3fadddb08490ff",
  "summary": "pending",
  "status": "pending",
  "merchant": "Kettle & Co",
  "amount": "129.00",
  "currency": "GBP",
  "reason": "Office kettle",
  "checks": [ /* one entry per rule */ ],
  "expires_at": "2026-10-13T14:09:20.883Z",
  "flags": [],
  "next_step": "A person has to approve this first. Check back with get_purchase. Do not send the request again."
}
The agent sees the payment details.get_payment_credential returns them as a tool result, so they pass through the model and whatever logs its provider keeps. They are for one purchase and work once, but treat that conversation as sensitive.

Purchases API

CallTokenWhat it does
POST/v1/intentsAgentAsk to make a purchase
GET/v1/intents/{id}Agent or approverRead a request and its audit trail
POST/v1/intents/{id}/decisionApproverApprove or decline
POST/v1/intents/{id}/credentialAgentOne-time payment details
POST/v1/intents/{id}/confirmationAgentReport the outcome

Asking to buy

FieldRequiredNotes
merchant.nameYes2 to 100 characters
merchant.urlNoA full https address. Rules about merchants match on the name or on the website, including its subdomains
merchant.countryCodeNoTwo capital letters, such as GB
merchant.categoryCodeNoFour-digit merchant category code
amountYesA decimal string such as "240.00", never a number, so nothing is lost to rounding
currencyYesGBP, EUR or USD, and it must match the company's rules
reasonYes3 to 200 characters. The approver reads it

Status

summary is for agents. status says exactly where a purchase is.

StatusSummaryMeaning
pendingpendingWaiting for the approver
approvedapprovedPayment details can be collected
issuingapprovedPayment details are being fetched right now
credential_issuedapprovedThe agent has the payment details
completedapprovedThe purchase happened
faileddeniedThe agent reported that it did not go through
denieddeniedStopped by a rule
declineddeniedThe approver said no
expireddeniedNot used in time

Rules a company can set

  • A limit per purchase, and a monthly budget per agent (calendar month, UTC).
  • An approval threshold. Set it to zero and every purchase waits for a person.
  • Approved merchants, blocked merchants, or both.
  • How long permission lasts before it lapses.
  • The named person who approves.

Approvals

When a purchase needs a person, the approver is sent a link, by a webhook into a chat channel for now, with email on the way. The link opens one screen: merchant, amount, reason, who is asking, the card, and what is left of the budget, with Approve and Decline.

CallTokenWhat it does
GET/v1/approvalsApproverEverything waiting for them
GET/v1/approvals/{id}Approver, or the linkThe purchase as the approver sees it, with the budget
POST/v1/approvals/{id}/decisionApprover, or the link{ "decision": "approve" } or "decline", with an optional reason

A webhook receives a JSON POST with a readable text line and the same facts as separate fields:

Webhook body
{
  "text": "Jamie's agent (for Jamie) wants to spend 240.00 GBP at Fernhill Software: Annual plan, design team\nApprove or decline: https://api.paycarter.com/approve/int_…#…",
  "event": "approval_requested",
  "intentId": "int_76c3efbc6ba3406e9fcb5b9d2fdd21ff",
  "merchant": "Fernhill Software",
  "amount": "240.00",
  "currency": "GBP",
  "reason": "Annual plan, design team",
  "requestedBy": "Jamie's agent",
  "onBehalfOf": "Jamie",
  "approver": "Priya, Finance",
  "expiresAt": "2026-10-13T14:09:20.711Z",
  "approvalUrl": "https://api.paycarter.com/approve/int_…#…"
}
Whoever holds an approval link can decide that purchase.The link works for one purchase, once, and only while it is waiting. It is never shown to the agent. Send it somewhere only the approver can read: a webhook into a shared channel lets everyone in that channel approve. Proper sign-in for approvers comes before launch.

Admin API

With an admin token. Everything is confined to the admin's own company, and anything belonging to another company is reported as not found.

CallBody
GETPOST/v1/admin/employeesname, email
GETPOST/v1/admin/approvers
PUT/v1/admin/approvers/{id}
name, and how to tell them: email, webhookUrl (https only)
GETPOST/v1/admin/policies
PUT/v1/admin/policies/{id}
currency, perPurchaseLimit, monthlyBudget, approvalThreshold, approverId. Optional: allowedMerchants, blockedMerchants, expiryHours. Amounts are strings such as "250.00"
GETPOST/v1/admin/cardsemployeeId, last4. Adds a test card. Real cards will be added by the cardholder, with the card network
GETPOST/v1/admin/agents
PUT/v1/admin/agents/{id}
name, employeeId, policyId, cardId. The card must belong to that employee
GETPOST/v1/admin/tokens
DELETE/v1/admin/tokens/{tokenId}
type (agent, approver or admin), subjectId, optional label. The token is in the reply to the POST and cannot be read again. Revoking takes effect at once
GET/v1/admin/intentsEvery purchase, newest first. Filter with ?status=pending,approved and ?limit= (up to 200)

Errors

Errors have one shape, with a stable code to branch on and a message written for a person:

400 Bad Request
{ "error": { "code": "invalid_currency", "message": "Currency must be one of GBP, EUR, USD." } }

A purchase the rules refuse is not an error. It comes back 201 with the summary denied, so the refusal is on the record.

HTTPCodeWhen
400invalid_amount, invalid_currency, invalid_merchant, invalid_reason, invalid_body, invalid_decision, invalid_status, invalid_querySomething in the request is wrong. The message says what
401unauthorisedNo token, or one that is unknown or revoked
403forbiddenThe token is the wrong kind for this call
404intent_not_found, not_foundNo such record, or it belongs to someone else
409not_approved, not_pending, no_credential, credential_already_issued, credential_in_progressThe purchase is not at the right stage for this call
409conflict, busyTwo requests collided. Read the purchase again and retry
502, 503rail_error, rail_unavailableThe payment network did not answer. Nothing was issued, and the purchase stays approved

Not there yet

What this preview does not do, so nobody finds out the hard way:

  • Real cards. It is waiting on the card networks. Until then every purchase runs on the test network.
  • Sign-in for approvers and admins. Both rest on tokens and links for now.
  • Safe retries. There are no idempotency keys yet. If a request to buy times out, read the purchase before sending it again.
  • Signed webhooks, and events beyond approval requests.
  • Usage limits and versioning. The address will gain a version policy before launch.