AI agents
A skill file and an MCP server, so an agent can wire up the integration and then verify its own work.
The integration is entirely scriptable, which makes it delegable. Two ways in, depending on what your agent can load.
The skill
One Markdown file describing the whole integration — the flows, the invariants, the failure modes, and the order to do things in. It is the same material as this site, written for a model rather than a reader.
mkdir -p .claude/skills/chromia-pay
curl -o .claude/skills/chromia-pay/SKILL.md https://pay-docs.chromia.com/skill.md
# then: "integrate Chromia Pay into this app"Reading these docs as Markdown
Every page here has a Markdown representation at the same URL, so an agent never has to parse HTML:
| URL | What you get |
|---|---|
/docs/webhooks.md | That one page as Markdown |
/llms.txt | An index of every page, for deciding what to read |
/llms-full.txt | The entire guide in one response |
/skill.md | The skill file above |
An Accept: text/markdown header on any /docs/* URL gets the same thing without the suffix.
The MCP server
Tools rather than prose. An agent can create a payment, register an endpoint, force a confirmation and read the event back — which means it can check its own integration end to end instead of asking you to.
{
"mcpServers": {
"chromia-pay": {
"command": "bunx",
"args": ["@chromia-pay/mcp"],
"env": {
"CPAY_SECRET_KEY": "cpay_sk_test_...",
"CPAY_API_URL": "https://pay-api.chromia.com"
}
}
}
}Tools
| Tool | Does |
|---|---|
create_payment | Create one and get its checkout_url |
get_payment | Status and attributed deposits |
cancel_payment | Cancel an unpaid one |
list_webhook_endpoints | With what each is subscribed to |
create_webhook_endpoint | Register a URL; returns the signing secret once |
set_webhook_events | Change a subscription without rotating the secret |
delete_webhook_endpoint | Stop delivering |
simulate_payment | Force confirmed / overpaid / underpaid / expired. Test keys only |
get_event | Re-read a canonical event |
Every tool above is a call to the same public API with the same key, so with a secret key this grants an agent nothing your own code could not do.
Give an agent a test key
Not because the tools are unsafe — they are the same API you are already exposing — but because an
agent iterating on an integration will create payments, and those should be payments that cannot move
real CHR. The server refuses simulate_payment outright with a live key, and says so.
Letting an agent run the store
Everything above is the integration. If you want an agent to operate the account — mint keys, set the payout address, change branding, manage who has access, work the reconciliation queue — that needs a different credential, and it is a genuinely different level of trust.
An agent token carries every action a dashboard admin can take. Mint one under Developers → Agent tokens, in the environment you want it to act in.
{
"mcpServers": {
"chromia-pay": {
"command": "bunx",
"args": ["@chromia-pay/mcp"],
"env": {
"CPAY_SECRET_KEY": "cpay_at_test_...",
"CPAY_API_URL": "https://pay-api.chromia.com"
}
}
}
}Same server, same variable. The prefix is what changes what it can reach: cpay_sk_ gets the tools
above, cpay_at_ gets those and the account tools below.
Account tools
| Tool | Does |
|---|---|
whoami | Which merchant, environment and mode, and whose behalf. Call it first |
list_api_keys / create_api_key / revoke_api_key | Secrets are returned once, at creation |
list_payout_wallets / set_payout_wallet / retire_payout_wallet | Where your CHR lands |
get_store_preferences / set_store_preferences | Underpayment tolerance, logo, accent colour |
list_team / grant_team_access / revoke_team_access | Who can open the dashboard |
list_environments / create_sandbox_environment | |
list_unattributed_deposits / attach_deposit_to_payment | The reconciliation queue |
read_audit_log | Every manual action, and whether a human or an agent took it |
list_agent_tokens / revoke_agent_token | Including revoking the one in use |
Minting a token is deliberately not one of them. A credential that can mint its own successors cannot be revoked — you revoke it, and the one it made keeps working — so the chain has to start with a person in the dashboard.
What an agent token can do that a key cannot
This is full CRUD over your account, and Chromia Pay is non-custodial, so none of it is reversible by us. Specifically:
- API keys it creates outlive it. Revoking the token does not revoke the keys.
- It can change your payout address without the ownership signature the dashboard asks for, which redirects every payment made after that point.
- It can grant dashboard access, and that grant also survives revocation.
Revoking stops it acting. It does not undo what it did — read_audit_log, filtered to the token, is
how you find what needs unwinding by hand. Every token expires, at most 90 days out; prefer the
shortest span that covers the work, and prefer a sandbox environment.
Attribution
Every action an agent takes is written to the audit log against the person who minted the token, marked as an agent, with the token's id:
{
"action": "wallet.set",
"actor": "you@yourstore.com",
"actor_kind": "agent",
"actor_token_id": "e67e6d5b-…",
"detail": "bsc payout address set by agent, ownership asserted"
}actor_email alone could not say this. "You revoked the live key" and "an agent you authorised revoked
the live key" are different events, and only one of them means you knew at the time.
Payout addresses an agent sets
set_payout_wallet requires unverified: true, because an agent cannot produce an ownership signature.
That is not a formality — it is the flag that makes the agent state what it is doing, and the address is
recorded as asserted rather than proved.
The same path exists for you in the dashboard, under "Add an address I can't sign with", and it exists because the proof is impossible for the address merchants most often want: you hold no key for an exchange deposit address and you cannot send from it.
Not an exchange address on Chromia
On the native rail, a payer's transfer carries Chromia Pay's own cpay:<REF> memo. An exchange that
needs its memo to credit your deposit receives ours instead, and the funds are lost inside the
exchange with no way for anyone to recover them. Use an address you control on Chromia; exchange
deposit addresses are fine on BNB Smart Chain and Base.