{"skill":{"id":"57959c60-acb5-4536-b10a-a27aa784ec84","name":"AskReceipts","author":"abodero","description":"A shared spending ledger for a human and their AI agents. Multiple autonomous agents (each with its own scoped key) file the purchases they make into one account; the human owner adds real photo/PDF/email receipts via AI extraction; and anyone queries the combined spend over MCP. Agent purchases carry an Agent Purchase tag so agent-vs-human spend stays separable. The headline use case: an agent calls get_spending_summary before a purchase and refuses if it would blow the budget — reasoning over a real cross-source ledger, not a hardcoded rule. It's the memory half of agent commerce (a spend-cap guard decides whether to pay; askReceipts remembers what was paid)","source_type":"content","source_url":null,"content":"# askReceipts — Agent Skill\r\n\r\n**askReceipts is the financial memory layer for agent commerce** — the universal\r\nreceipt inbox and spending memory for autonomous AI agents. Any agent can file\r\nthe receipts for the purchases it makes and later ask how much it has spent —\r\nover MCP, headlessly, with no human in the loop. (Where a spend-cap guard decides\r\n_whether_ an agent may pay, askReceipts is the ledger that remembers _what it\r\npaid_.)\r\n\r\nIt's a **shared ledger for a human and their AI agents.** Multiple autonomous\r\nagents — each with its own scoped key — file the purchases they make into one\r\naccount; the human owner adds real-world receipts (photo, PDF, email) via AI\r\nextraction; and any of them can query the **combined** spend, with agent\r\npurchases tagged so agent-vs-human spend stays separable. It's the shared memory\r\nwhere a whole fleet of agents and their human converge. _(This public demo token\r\nis scoped to agent purchases via `mpp`; the real-receipt ingestion paths run on\r\nyour own account — see below.)_\r\n\r\nThis file tells you (an AI agent) how to call the askReceipts MCP server on your\r\nown. Everything below is self-serve: authenticate with the bearer key, then call\r\nthe tools.\r\n\r\n---\r\n\r\n## Connect\r\n\r\n- **Transport:** MCP over Streamable HTTP\r\n- **Endpoint:** `https://www.askreceipts.com/api/mcp`\r\n\r\n### Quick start — public demo, zero signup\r\n\r\nUse the token `ar_demo_nandahack` **directly** as your bearer. It's a **public,\r\nnon-secret** token — nothing to request, no environment variable to set. Copy\r\neither form below and start calling tools immediately.\r\n\r\nMCP client config (generic shape; the exact form is client-specific):\r\n\r\n```jsonc\r\n{\r\n  \"mcpServers\": {\r\n    \"askreceipts\": {\r\n      \"url\": \"https://www.askreceipts.com/api/mcp\",\r\n      \"headers\": { \"Authorization\": \"Bearer ar_demo_nandahack\" },\r\n    },\r\n  },\r\n}\r\n```\r\n\r\nOr, in **Claude Code**, add it from the shell:\r\n\r\n```bash\r\nclaude mcp add askreceipts --transport http \\\r\n  https://www.askreceipts.com/api/mcp \\\r\n  --header \"Authorization: Bearer ar_demo_nandahack\"\r\n```\r\n\r\n> **Restart after adding.** Most MCP clients (Claude Code, Desktop, Cursor) load\r\n> their server list at session start, so a server added mid-session won't expose\r\n> its tools until you start a **new session**. Add askReceipts to your config\r\n> first, then open the session — \"start calling tools immediately\" applies once\r\n> the client has loaded it, not to the session in which you ran `mcp add`.\r\n\r\n`ar_demo_nandahack` maps every caller to a **shared, seeded demo account** —\r\nthrowaway, safe to read from and write to, and reset periodically. It is not\r\nscoped to any real user, so treat the data as a communal sandbox that other\r\nevaluators are also writing to. The demo token accepts agent purchases via\r\n`save_receipt` with `source_type: mpp` plus all read tools; the AI-extraction\r\npaths (`html`/`pdf`, `upload_receipt`) require your own key (below).\r\n\r\n### Use your own account instead (optional)\r\n\r\nFor a private, single-account setup, request an agent key (prefixed `ar_agent_`)\r\nand send it as the bearer instead of the demo token — ideally injected from an\r\nenvironment variable so the raw value stays out of the skill file:\r\n\r\n```bash\r\nclaude mcp add askreceipts --transport http \\\r\n  https://www.askreceipts.com/api/mcp \\\r\n  --header \"Authorization: Bearer $ASKRECEIPTS_AGENT_KEY\"\r\n```\r\n\r\nAn agent key is headless (no OAuth, no browser) and resolves to exactly one\r\naccount; every tool call operates only on that account's receipts, never anyone\r\nelse's.\r\n\r\n<!--\r\nFuture / NANDA-native option (not required for judging):\r\nexpose a self-service key endpoint so an agent can mint its own scoped, expiring\r\nkey (a KYA-lite version of the `hackathon login` pattern), moving toward NANDA's\r\nKnow-Your-Agent identity model (mutual-TLS + OIDC + public keys) instead of a\r\nshared static key.\r\n-->\r\n\r\n### Verify the connection first\r\n\r\nCall **`ping`** (no arguments). Expect: `pong — authenticated as <userId>`.\r\nIf you get an auth error, the bearer key is missing or wrong.\r\n\r\n---\r\n\r\n## The 60-second autonomous loop (what a judge should run)\r\n\r\n```\r\nping                 → confirm you're authenticated\r\nsave_receipt         → file a purchase (agent-made or sample)\r\nask_receipts         → \"how much have I spent this month?\"  (you reason over the data)\r\nget_spending_summary → authoritative totals by category / merchant / month\r\n```\r\n\r\nConcretely:\r\n\r\n1. **`ping`** → confirm auth.\r\n2. **`save_receipt`** with a sample MPP purchase (see below). An `mpp` receipt is\r\n   stored **immediately as `processed`** and returned with its `receiptId` — no\r\n   polling needed. (`html`/`pdf` receipts instead return `pending` and are queued\r\n   for AI extraction; poll `get_receipt` for those.)\r\n3. **`ask_receipts`** with `{ \"question\": \"how much have I spent this month?\",\r\n\"format\": \"context\" }` → returns an authoritative spending summary + the\r\n   relevant receipts so _you_ compose the answer.\r\n4. Optionally **`get_spending_summary`** for exact totals with no reasoning\r\n   needed.\r\n\r\n### Walkthrough — a cold agent, start to finish\r\n\r\nConnected with the demo token, call these four tools in order. Responses are\r\n**representative** — exact totals vary because the demo account is a shared\r\nsandbox that resets periodically.\r\n\r\n**1 · Confirm auth** — `ping` (no arguments):\r\n\r\n```\r\npong — authenticated as user_3FpOCPvpSoy101L2EwTGrcDtjvg\r\n```\r\n\r\n**2 · File an agent purchase** — `save_receipt`. An `mpp` receipt is stored\r\n**immediately as `processed`** (no polling).\r\n\r\n> **Generate a UNIQUE `payment_intent_id` each time** — it's the dedup key. On the\r\n> shared demo account, a fixed example id another evaluator already filed will come\r\n> back as `\"Duplicate receipt detected\"` (that's dedup working, not a failure).\r\n> Use a fresh random value, e.g. `pi_demo_` + a random suffix.\r\n\r\n```jsonc\r\n// arguments — replace pi_demo_<random> with your own unique id\r\n{\r\n  \"source_type\": \"mpp\",\r\n  \"receipt_header\": \"{\\\"merchant\\\":\\\"Blue Bottle Coffee\\\",\\\"amount\\\":4.50,\\\"currency\\\":\\\"USD\\\",\\\"timestamp\\\":\\\"2026-07-10T14:30:00Z\\\",\\\"payment_method\\\":\\\"card\\\",\\\"payment_intent_id\\\":\\\"pi_demo_a1b2c3d4\\\",\\\"line_items\\\":[{\\\"description\\\":\\\"Latte\\\",\\\"amount\\\":4.50}]}\",\r\n  \"purchased_by\": \"my-agent\",\r\n}\r\n```\r\n\r\n```json\r\n// response\r\n{\r\n  \"receiptId\": \"jh7abc…\",\r\n  \"status\": \"processed\",\r\n  \"merchant\": \"Blue Bottle Coffee\",\r\n  \"amount\": \"4.50\",\r\n  \"currency\": \"USD\",\r\n  \"transactionDate\": \"2026-07-10\"\r\n}\r\n```\r\n\r\nRe-send the same `payment_intent_id` and it refuses — no double-count:\r\n\r\n```json\r\n{ \"status\": \"error\", \"note\": \"Duplicate receipt detected — not saved again\" }\r\n```\r\n\r\n**3 · Ask about spend** — `ask_receipts` with `format: \"context\"`, then reason over\r\nthe returned data yourself:\r\n\r\n```jsonc\r\n// arguments\r\n{\r\n  \"question\": \"how much have I spent on coffee this month?\",\r\n  \"format\": \"context\",\r\n}\r\n```\r\n\r\n```json\r\n// response (abridged — shape is stable, values vary)\r\n{\r\n  \"summary\": {\r\n    \"totalCount\": 13,\r\n    \"byCurrency\": { \"USD\": { \"count\": 13, \"total\": 1122.07 } }\r\n  },\r\n  \"recent\": [\r\n    {\r\n      \"merchantName\": \"Blue Bottle Coffee\",\r\n      \"transactionAmount\": \"4.50\",\r\n      \"currency\": \"USD\",\r\n      \"transactionDate\": \"2026-07-10\"\r\n    }\r\n  ],\r\n  \"searchHits\": []\r\n}\r\n```\r\n\r\n**4 · Get authoritative totals** — `get_spending_summary` (no arguments), when you\r\nwant exact rollups with no reasoning:\r\n\r\n```json\r\n// response (abridged)\r\n{\r\n  \"totalCount\": 13,\r\n  \"byCurrency\": { \"USD\": { \"count\": 13, \"total\": 1122.07 } },\r\n  \"byCategory\": [\r\n    { \"category\": \"Restaurants\", \"count\": 1, \"currency\": \"USD\", \"total\": 4.5 }\r\n  ],\r\n  \"topMerchants\": [\r\n    { \"merchant\": \"Publix\", \"count\": 3, \"currency\": \"USD\", \"total\": 250.78 }\r\n  ],\r\n  \"byMonth\": [\r\n    { \"month\": \"2026-07\", \"currency\": \"USD\", \"count\": 4, \"total\": 255.28 }\r\n  ],\r\n  \"dateRange\": { \"earliest\": \"2026-06-05\", \"latest\": \"2026-07-10\" }\r\n}\r\n```\r\n\r\nThat's the whole loop — authenticate, file, ask, reconcile — fully headless. Totals\r\nreconcile across `get_spending_summary`, `get_receipt_count`, and `ask_receipts`'\r\n`summary`, so a budget check reading any of them gets the same number.\r\n\r\n### Filing a receipt — `save_receipt`\r\n\r\nThree source types; pick one:\r\n\r\n```jsonc\r\n// MPP — an agent purchase. `receipt_header` is a JSON STRING of the fields below.\r\n// Use a UNIQUE payment_intent_id per purchase (see note below).\r\n{\r\n  \"source_type\": \"mpp\",\r\n  \"receipt_header\": \"{\\\"merchant\\\":\\\"Blue Bottle Coffee\\\",\\\"amount\\\":2.00,\\\"currency\\\":\\\"USD\\\",\\\"timestamp\\\":\\\"2026-07-08T14:30:00Z\\\",\\\"payment_method\\\":\\\"card\\\",\\\"payment_intent_id\\\":\\\"pi_<unique-per-purchase>\\\",\\\"line_items\\\":[{\\\"description\\\":\\\"Latte\\\",\\\"amount\\\":2.00}]}\",\r\n  \"purchased_by\": \"your-agent-name\"\r\n}\r\n\r\n// HTML receipt (Toast POS, Stripe email, etc.)\r\n{ \"source_type\": \"html\", \"html\": \"<raw HTML string>\" }\r\n\r\n// PDF receipt\r\n{ \"source_type\": \"pdf\",  \"base64\": \"<base64 PDF bytes, <=10MB>\", \"filename\": \"receipt.pdf\" }\r\n```\r\n\r\n**⚠️ `amount` is in WHOLE currency units, not minor units.** `2.00` means **$2.00**,\r\nnot 2 cents. Do **not** send Stripe-style integer cents (`200` would be stored as\r\n**$200.00**). Same for each `line_items[].amount`.\r\n\r\nMPP `receipt_header` fields (all inside the JSON string):\r\n\r\n| Field               | Required    | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n| ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| `merchant`          | recommended | falls back to `merchant_hint`, else \"Unknown Merchant\". Blank/whitespace-only counts as missing and triggers the same fallback                                                                                                                                                                                                                                                                                                                                                                      |\r\n| `amount`            | yes         | **whole units** (dollars), e.g. `2.00`. Send a **plain number** — `1000.00`, not `\"1,000.00\"`, `\"$1,000\"`, or other formatting (a malformed numeric string is **rejected**, never silently truncated). Must be positive and ≤ 1,000,000; if omitted/≤0, above the cap, or rounding to 0.00 (e.g. `0.001`) the call is **rejected** (falls back to the sum of `line_items` when possible) — nothing is stored. Sub-cent values that still round to ≥ 0.01 are rounded to 2 dp with a `warnings` note |\r\n| `currency`          | no          | ISO code, default `USD` (not validated against ISO-4217 — send a real code)                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n| `timestamp`         | no          | ISO 8601, default now. Rejected (nothing stored) if unparseable, more than a day in the future, or before 2000-01-01                                                                                                                                                                                                                                                                                                                                                                                |\r\n| `payment_method`    | no          | free text, default `mpp`                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n| `payment_intent_id` | no          | authoritative dedup key — use a **unique value per purchase** (also accepted as a top-level arg); on the shared demo account, reusing an id another evaluator filed returns a \"Duplicate\" response — see Dedup below                                                                                                                                                                                                                                                                                |\r\n| `line_items`        | no          | `[{ \"description\", \"amount\" }]`, amounts also in whole units                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n\r\n`purchased_by` and `notes` are optional; they are recorded on the archived\r\nreceipt (in the stored receipt file), not returned as separate query fields.\r\n\r\n> **`save_receipt` is permanent** — there is no delete/undo tool exposed here, so\r\n> get the amount and merchant right on the first call. If you file a wrong amount,\r\n> it stays in the ledger and skews totals.\r\n\r\n**Dedup:** deduplication keys strictly on `payment_intent_id`. If you resend an\r\nintent that is already filed, no second row is created and the originally-filed\r\nreceipt stands:\r\n\r\n- **Same merchant + amount + currency** → response `status: \"error\"`, `note:\r\n\"Duplicate receipt detected — not saved again\"` (an idempotent resend).\r\n- **Different amount, merchant, or currency** → response `status: \"error\"` with a\r\n  `note` explaining the intent is already filed with different details, and\r\n  **nothing is saved**. A payment intent is unique per payment, so a re-derived\r\n  amount or a slightly different merchant string will not double-file it.\r\n\r\nTwo **distinct** `payment_intent_id`s always produce two rows, even if the\r\nmerchant, amount, currency, and date are identical (e.g. the same coffee bought\r\ntwice in one day) — the intent, not the content, is the uniqueness key. The\r\nintent is whitespace-trimmed but case-sensitive (Stripe IDs are case-stable).\r\n\r\nReceipts sent **without** a `payment_intent_id` fall back to a\r\nmerchant + date + amount duplicate check (which _will_ treat a same-day, same-\r\namount repeat as a duplicate), so always pass the intent ID when you have one.\r\n\r\nWhen a save succeeds but a value was coerced (sub-cent amount rounded, the\r\nstated amount disagreed with the `line_items` sum, or a `line_items` amount was\r\nnon-numeric and counted as 0), the response includes a `warnings` array\r\ndescribing what happened.\r\n\r\n**Categories are assigned asynchronously**, a few seconds after an `mpp` receipt\r\nis stored. An immediate `get_receipt` may show no `categories` yet; they populate\r\nshortly after. Every agent purchase also gets an `Agent Purchase` system tag (so\r\nagent spend is findable via `search_receipts`); that tag is intentionally\r\n**excluded** from `get_spending_summary`'s category breakdown so totals reconcile.\r\n\r\n### Asking about spend — `ask_receipts`\r\n\r\n```jsonc\r\n{\r\n  \"question\": \"how much have I spent on restaurants this month?\",\r\n  \"format\": \"context\", // \"answer\" (server default) or \"context\" (pass this if you're an agent)\r\n  \"sessionId\": \"optional-conversation-id\",\r\n}\r\n```\r\n\r\n- **`format: \"context\"`** — returns machine-readable JSON: an authoritative\r\n  `summary` (totals over ALL receipts), plus `recent` and `searchHits` samples so\r\n  you can cite specific receipts and reason yourself. Prefer this if you are an\r\n  agent. The payload is capped and slimmed to stay within your token budget;\r\n  `recentTruncated` / `searchHitsTruncated` tell you when to narrow via\r\n  `search_receipts`. Line items are omitted — fetch one receipt's items with\r\n  `get_receipt`. **Read tools rename the write fields** — `recent`/`searchHits`\r\n  rows use `merchantName`/`transactionAmount`, not the `merchant`/`amount` you\r\n  sent to `save_receipt`. See \"Read-tool response shapes\" under Tool reference.\r\n- **`format: \"answer\"`** — the server writes a prose answer for you.\r\n\r\n> **Budget-enforcement pattern (the money shot):** before an agent makes a\r\n> purchase, call `get_spending_summary` (or `ask_receipts`), compare the category\r\n> total against the user's budget, and **refuse** if the purchase would exceed\r\n> it. This is reasoning over a real, cross-source ledger — not a hardcoded rule.\r\n\r\n---\r\n\r\n## Tool reference\r\n\r\n| Tool                                                                                                                                 | Purpose                                                                        |\r\n| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |\r\n| `ping`                                                                                                                               | Health check — confirms the server is reachable and your key is valid.         |\r\n| `save_receipt`                                                                                                                       | File a receipt from an agent purchase or external source (mpp / html / pdf).   |\r\n| `ask_receipts`                                                                                                                       | Ask a natural-language question about receipts, or create a spending reminder. |\r\n| `get_spending_summary`                                                                                                               | Authoritative totals: by currency, month, category, top merchants.             |\r\n| `search_receipts`                                                                                                                    | Find receipts by merchant, date range, amount, or category.                    |\r\n| `list_receipts`                                                                                                                      | Page through processed receipts, most recent first.                            |\r\n| `get_receipt`                                                                                                                        | Full detail (incl. line items) for one receipt by id.                          |\r\n| `get_receipt_count`                                                                                                                  | Total number of receipts.                                                      |\r\n| `get_receipt_url`                                                                                                                    | Signed URL to the original receipt file.                                       |\r\n| `upload_receipt`                                                                                                                     | Fetch a receipt file from a URL and queue it for processing.                   |\r\n| `list_reminders` / `create_reminder` / `dismiss_reminder` / `delete_reminder` / `reactivate_reminder` / `get_reminder_notifications` | Manage spending/return-window reminders.                                       |\r\n\r\n### Read-tool response shapes (field names differ from `save_receipt`)\r\n\r\nThe **write** response (`save_receipt`) and the **read** tools use different field\r\nnames for the same values. Match on the read-tool names when reconciling — don't\r\nassume the `save_receipt` keys carry over.\r\n\r\n| Value          | `save_receipt` returns        | `get_receipt` / `search_receipts` / `list_receipts` return |\r\n| -------------- | ----------------------------- | ---------------------------------------------------------- |\r\n| receipt id     | `receiptId`                   | `id` (search/list) · `receiptId` accepted as the arg       |\r\n| merchant       | `merchant`                    | `merchantName`                                             |\r\n| amount         | `amount` (string)             | `transactionAmount` (string)                               |\r\n| currency, date | `currency`, `transactionDate` | `currency`, `transactionDate` (same)                       |\r\n| categories     | (async, not returned)         | `categories` (array; includes the `Agent Purchase` tag)    |\r\n\r\n- **`get_receipt`** takes `{ receiptId }` and returns the full row incl. `line_items`.\r\n- **`search_receipts`** params: `merchant`, `fromDate`, `toDate`, `minAmount`,\r\n  `maxAmount`, `category`, `limit`. The `merchant` filter is **fuzzy / token-ranked,\r\n  not a strict substring** — a short token can match more rows than you expect, so\r\n  filter/verify the hits client-side when you need exact matches.\r\n- **`list_receipts`** params: `limit`, `cursor` (page most-recent-first).\r\n\r\n---\r\n\r\n## Rules of the road\r\n\r\n- Every call operates only on the account the bearer key belongs to. You cannot\r\n  see or touch another user's receipts.\r\n- Totals come from `get_spending_summary` / the `summary` block — never sum the\r\n  `recent` / `searchHits` samples; they are capped.\r\n- A receipt can have **more than one category**, so the `byCategory` rows can\r\n  overlap and need not sum to the currency total — use `byCurrency` (or the\r\n  `total`) for the true overall figure, not a sum of categories.\r\n- `save_receipt` with `source_type: mpp` is stored **immediately as `processed`**\r\n  (no polling). `html`/`pdf` return `pending` and are processed asynchronously —\r\n  poll `get_receipt` for those.\r\n- Amounts are grouped by currency and never summed across currencies.","endpoints":"https://www.askreceipts.com/api/mcp","tags":"receipts, spending, payments, agent-commerce, budget, mcp","reachable":null,"created_at":"2026-07-10T13:40:53.596Z"}}