Pink sits between an AI agent and a company's money. The agent asks "may I pay this?"; the company's rules answer in under a second: allow ask a person block. This sandbox is a full copy of that engine with test credentials, so any MCP-capable agent can try it in minutes.
Pick a company template. You get an admin key for the approvals console and one key per AI agent.
Hosted apps (Claude.ai, ChatGPT, Copilot Studio): add https://agentic-api.pinkwallet.com/mcp as a connector. A Pink sign-in page opens: paste your admin key, pick which agent the app acts as, click Allow. That is OAuth 2.1 with PKCE; tokens expire hourly and renew for 30 days.
IDEs, SDKs and scripts: use an agent key directly, as a Bearer header or in the URL.
OAuth (hosted apps): https://agentic-api.pinkwallet.com/mcp ← paste this, then sign in
Agent key (SDKs): https://agentic-api.pinkwallet.com/mcp
Authorization: Bearer <agent_key>
Key in URL: https://agentic-api.pinkwallet.com/mcp/<agent_key>
Tools the agent receives: pink.get_budget pink.list_payees pink.list_rules pink.check_policy pink.request_payment pink.get_credential pink.report_receipt
pink.request_payment({ payee_id, amount, purpose, reason, evidence }). It never sees a card or a bank login, only the answer.hold_id; a person approves in the console or on their phone and the agent polls pink.get_credential. blocked returns the reason and nothing else.pink.report_receipt closes the loop and raises the agent's trust score.claude mcp add --transport http pink \ https://agentic-api.pinkwallet.com/mcp/<agent_key>
Then ask Claude: "Check my budget with Pink, then buy 20 kg of beans from Counter Culture Coffee for $420."
{ "mcpServers": { "pink": {
"url": "https://agentic-api.pinkwallet.com/mcp",
"headers": { "Authorization": "Bearer <agent_key>" }
} } }Add a custom connector and paste https://agentic-api.pinkwallet.com/mcp. The app discovers Pink's OAuth server, opens the Pink sign-in page, you paste the workspace admin key and choose the agent, click Allow. The key-in-URL form also still works for a quick test.
from agents.mcp import MCPServerStreamableHttp
pink = MCPServerStreamableHttp(params={
"url": "https://agentic-api.pinkwallet.com/mcp",
"headers": {"Authorization": "Bearer <agent_key>"}})"mcp_servers": [{ "type": "url", "name": "pink",
"url": "https://agentic-api.pinkwallet.com/mcp",
"authorization_token": "<agent_key>" }]curl -X POST https://agentic-api.pinkwallet.com/v1/payments \
-H "Authorization: Bearer <agent_key>" \
-H "Content-Type: application/json" \
-d '{"payee_id":"p_cc","amount":420,"purpose":"20 kg beans"}'| Method | Path | Key | What it does |
|---|---|---|---|
| POST | /v1/sandbox/workspaces | none | Create a workspace: {company, email, template} → admin key + agent keys |
| GET | /v1/me · /v1/budget · /v1/payees · /v1/vaults · /v1/rules | agent | What this agent is, may spend, may pay, and the rules that apply to it |
| POST | /v1/payments/check | agent | Dry run: would_allow / would_ask / would_block with the full trace |
| POST | /v1/payments | agent | Request a payment. 201 allowed · 202 pending_human · 403 blocked. Send Idempotency-Key to make retries safe |
| GET | /v1/payments/{id} | agent | Poll a hold; returns the credential once approved |
| POST | /v1/payments/{id}/receipt | agent | File the receipt |
| GET | /.well-known/oauth-protected-resource/mcp · /.well-known/oauth-authorization-server | none | OAuth discovery (RFC 9728 / 8414). POST /register dynamic client registration, /authorize consent, POST /token code + PKCE and refresh |
| GET | /v1/admin/state | admin | Everything: agents, vaults, payees, rules, pending requests, transactions |
| POST | /v1/admin/requests/{id}/approve · /decline | admin | Decide a pending request (the console uses this) |
| PUT | /v1/admin/rules | admin | Replace the policy: {rules:[…]}. Publishes a new version |
| PATCH | /v1/admin/agents/{id} | admin | {status:"paused"|"active", monthly, perPay} |
| POST | /v1/admin/reset-day · /v1/admin/reset | admin | Zero today's spend · reset the workspace to its template |
| payee_id | Id from the payee list. Omit and pass payee_name for a payee not on the list (the policy will usually stop and ask). |
| amount · currency | Positive number, USD by default. Exactly one of USD, EUR, GBP, HKD, SGD, JPY (case-insensitive, no padding); anything else is rejected with 400 and nothing is evaluated. Rule thresholds are USD; other currencies are converted before matching. Rules can be currency-specific. |
| purpose · reason | What it is for, and why now. Both are shown to the person who approves. |
| evidence | Free-text references: PO number, invoice id, ticket, photos. |
| evidence_flags | Structured signals the rules react to: po sow ticket scan statement brief renewal dupInvoice payeeChanged repeatCustomer deposit. In production these come from connected systems (ERP, helpdesk, WMS); in the sandbox the agent states them. |
| local_hour | Dry run only (/v1/payments/check, pink.check_policy): simulate the hour of day, 0–23. A real payment ignores it: the server reads its own clock in the rule's timezone (tz on the rule, shown by GET /v1/rules). |
| idempotency_key | Any unique string per attempt. Repeating it returns the original decision instead of paying twice. |
Sandbox credentials are test values (4111… cards, PWS- transfer references). No money moves. Workspaces are kept for 30 days of inactivity. Questions and early access: agentic@pinkwallet.com