# Talo Partner Hub > Bank-transfer payments in Argentina (ARS, one CVU and alias per payment) on a Talo Pay account, for code, terminals and AI agents: REST API, `talo-hub` CLI, a remote MCP server with OAuth, and agent skills. An agent can test everything in sandbox by itself (simulated transfers, webhook pings, a go-live checklist) and take it to production. Trust: run by an official partner of Talo Pay; the money stays in the merchant's Talo account. Who operates it, what it stores (secrets encrypted) and how to revoke or disconnect: https://talopartnerhub.vercel.app/docs/security.md Connect: MCP `https://talopartnerhub.vercel.app/api/mcp` with OAuth (the person picks sandbox or production and the permissions; no key in the chat), or `Authorization: Bearer tph_test_…` (sandbox) / `tph_live_…` (production). Key scopes: read, charge, full. Every page below is Markdown. Human pages under https://talopartnerhub.vercel.app/docs also answer `Accept: text/markdown`. ## Docs - [Talo Partner Hub for agents](https://talopartnerhub.vercel.app/docs/index.md): Start here: what the hub does, how to connect, and where every page is. - [Quickstart](https://talopartnerhub.vercel.app/docs/quickstart.md): From nothing to a paid sandbox payment in five commands. - [Sandbox: test everything yourself](https://talopartnerhub.vercel.app/docs/sandbox.md): Simulated transfers, every payment status, webhook pings, and the end-to-end test plan. - [Credentials: sandbox and production, safely](https://talopartnerhub.vercel.app/docs/credentials.md): Key types and scopes, where to keep them, least privilege, rotation, and what never to do. - [Security: what the hub stores and how to revoke it](https://talopartnerhub.vercel.app/docs/security.md): Who operates the hub, what it keeps (secrets encrypted), what each key can do, and how to revoke one key or disconnect the whole account. - [From sandbox to production](https://talopartnerhub.vercel.app/docs/go-live.md): The readiness checklist and the exact steps to start charging real money. - [MCP server](https://talopartnerhub.vercel.app/docs/mcp.md): Connect with OAuth or a key, and every tool with its scope. - [CLI](https://talopartnerhub.vercel.app/docs/cli.md): talo-hub commands, environments and non-interactive use. - [REST API](https://talopartnerhub.vercel.app/docs/api.md): Every endpoint with its fields, the payment object, errors and payment statuses. - [Webhooks](https://talopartnerhub.vercel.app/docs/webhooks.md): Events, the exact signature format, verification code, retries, ordering and a test vector. - [Checkout: payment links and the transfer widget](https://talopartnerhub.vercel.app/docs/checkout.md): Talo's payment page, reusable payment links (/l/, with a QR) and the widget or shadcn component, with the read-only checkout_token behind it. - [Integrate on any stack](https://talopartnerhub.vercel.app/docs/stacks.md): One kit of best practices per stack (Next.js + Vercel, Node, Python, Laravel, edge), the contract every integration meets, and how to port it to any other stack. - [Skills](https://talopartnerhub.vercel.app/docs/skills.md): SKILL.md files that teach an agent to integrate and operate Talo. ## Skills - [talo-payments](https://talopartnerhub.vercel.app/skills/talo-payments/SKILL.md): El agente integra el checkout por transferencia: crea el pago, muestra el CVU, verifica webhooks firmados y prueba todo en sandbox. - [talo-go-live](https://talopartnerhub.vercel.app/skills/talo-go-live/SKILL.md): El agente prueba el flujo completo en sandbox, maneja las credenciales de los dos entornos con seguridad y lleva la integración a producción. - [talo-operations](https://talopartnerhub.vercel.app/skills/talo-operations/SKILL.md): El agente consulta pagos, reembolsa, revisa saldo y movimientos, y prueba flujos en sandbox con el CLI o el MCP. Stack kits, one per stack (`npx talo-hub skills install` adds the one that matches the project; [stacks](https://talopartnerhub.vercel.app/docs/stacks.md) says how to adapt to any other stack): - [talo-stack-nextjs-vercel](https://talopartnerhub.vercel.app/skills/talo-stack-nextjs-vercel/SKILL.md): Next.js + Vercel - [talo-stack-node-express](https://talopartnerhub.vercel.app/skills/talo-stack-node-express/SKILL.md): Node.js (Express, Fastify, NestJS, Koa) - [talo-stack-python](https://talopartnerhub.vercel.app/skills/talo-stack-python/SKILL.md): Python (FastAPI, Django, Flask) - [talo-stack-laravel](https://talopartnerhub.vercel.app/skills/talo-stack-laravel/SKILL.md): PHP + Laravel - [talo-stack-edge](https://talopartnerhub.vercel.app/skills/talo-stack-edge/SKILL.md): Edge (Cloudflare Workers, Supabase Edge Functions, Deno) ## Checkout without building one - [Payment links](https://talopartnerhub.vercel.app/docs/checkout.md): a reusable `/l/` URL and QR, with a fixed price or a range the buyer picks; each buyer who presses Pagar gets a new payment on Talo's own page (CVU and alias). `POST /api/v1/payment-links`, `talo-hub links create`, MCP `create_payment_link`. - [Transfer widget](https://talopartnerhub.vercel.app/docs/checkout.md): the payment on the merchant's own page, live. `` and `` with the `checkout_token` from the payment answer, a read-only token for that one payment (15 days). - shadcn registry: `npx shadcn@latest add https://talopartnerhub.vercel.app/r/talo-checkout-nextjs.json` (Next.js App Router: server-only hub client, signed webhook route, React status component), or just the component: https://talopartnerhub.vercel.app/r/talo-payment-status.json. - The widget and the link pages are only UI: fulfil orders on the signed webhook, never on a browser event. ## Try it - [Demo shop](https://talopartnerhub.vercel.app/demo): a sandbox store that charges through the hub like any merchant (public API with its own key, signed webhooks). Buy something, then pay it with `npx talo-hub sandbox pay ` and watch the order confirm itself. Its order page shows the payment with the `` widget. ## Machine-readable - [OpenAPI 3.1](https://talopartnerhub.vercel.app/openapi.json) - [Skills index](https://talopartnerhub.vercel.app/skills/index.json) - [MCP server card](https://talopartnerhub.vercel.app/.well-known/mcp.json) - [OAuth protected resource metadata](https://talopartnerhub.vercel.app/.well-known/oauth-protected-resource) - [Health](https://talopartnerhub.vercel.app/api/health) ## Optional - [Full reference and skills in one file](https://talopartnerhub.vercel.app/llms-full.txt) --- # Talo Partner Hub for agents > Start here: what the hub does, how to connect, and where every page is. Bank-transfer payments in Argentina on a Talo Pay account: each payment gets its own CVU and alias, the buyer transfers ARS from any bank, and the hub tells you when it lands. Everything works the same in **sandbox** (test money, simulated transfers) and **production** (real money). ## The fastest path 1. Connect the MCP server: `https://talopartnerhub.vercel.app/api/mcp`. Clients with OAuth (Claude Code, Claude Desktop, Cursor) open the browser for the person to authorize; no key is pasted anywhere. See [mcp](https://talopartnerhub.vercel.app/docs/mcp.md). 2. Test the whole flow in sandbox yourself: create_payment → simulate_transfer → get_payment / list_events. See [sandbox](https://talopartnerhub.vercel.app/docs/sandbox.md). 3. Integrate in code with the REST API and signed webhooks. See [api](https://talopartnerhub.vercel.app/docs/api.md) and [webhooks](https://talopartnerhub.vercel.app/docs/webhooks.md). 4. Run check_go_live (or GET /api/v1/readiness) until it says ready, then repeat in production. See [go-live](https://talopartnerhub.vercel.app/docs/go-live.md). A person has to authorize the Talo account once per environment (Talo requires it). After that, you can do everything else: create payments, simulate transfers, register and ping webhooks, create narrower keys, check readiness. No checkout to build? A reusable payment link (`/l/`, with a QR) sells with no code, and the `` widget shows a payment on the merchant's own page with one script tag. See [checkout](https://talopartnerhub.vercel.app/docs/checkout.md). `npx talo-hub login` waits until the person authorizes and then exits by itself (0 = connected): that exit is your signal, so don't ask them whether they authorized. Show them the link and code it prints; they compare the code in their browser. If your tool notifies you when a background command exits (Claude Code's background commands do), run it in the background and continue when that notice arrives; otherwise run it in the foreground with a timeout of at least 10 minutes. If it gets cut off, run the same command again: it picks up the same login, with the same link and code. If it expired, running it again checks once whether the person finished in Talo anyway (and collects the key), and otherwise starts a new login with a new link and code. The hub is run by an official partner of Talo Pay and the money stays in the merchant's Talo account; [security](https://talopartnerhub.vercel.app/docs/security.md) says who operates it, what it stores and how the person revokes keys or disconnects the account. ## Pages - [Quickstart](https://talopartnerhub.vercel.app/docs/quickstart.md): From nothing to a paid sandbox payment in five commands. - [Sandbox: test everything yourself](https://talopartnerhub.vercel.app/docs/sandbox.md): Simulated transfers, every payment status, webhook pings, and the end-to-end test plan. - [Credentials: sandbox and production, safely](https://talopartnerhub.vercel.app/docs/credentials.md): Key types and scopes, where to keep them, least privilege, rotation, and what never to do. - [Security: what the hub stores and how to revoke it](https://talopartnerhub.vercel.app/docs/security.md): Who operates the hub, what it keeps (secrets encrypted), what each key can do, and how to revoke one key or disconnect the whole account. - [From sandbox to production](https://talopartnerhub.vercel.app/docs/go-live.md): The readiness checklist and the exact steps to start charging real money. - [MCP server](https://talopartnerhub.vercel.app/docs/mcp.md): Connect with OAuth or a key, and every tool with its scope. - [CLI](https://talopartnerhub.vercel.app/docs/cli.md): talo-hub commands, environments and non-interactive use. - [REST API](https://talopartnerhub.vercel.app/docs/api.md): Every endpoint with its fields, the payment object, errors and payment statuses. - [Webhooks](https://talopartnerhub.vercel.app/docs/webhooks.md): Events, the exact signature format, verification code, retries, ordering and a test vector. - [Checkout: payment links and the transfer widget](https://talopartnerhub.vercel.app/docs/checkout.md): Talo's payment page, reusable payment links (/l/, with a QR) and the widget or shadcn component, with the read-only checkout_token behind it. - [Integrate on any stack](https://talopartnerhub.vercel.app/docs/stacks.md): One kit of best practices per stack (Next.js + Vercel, Node, Python, Laravel, edge), the contract every integration meets, and how to port it to any other stack. - [Skills](https://talopartnerhub.vercel.app/docs/skills.md): SKILL.md files that teach an agent to integrate and operate Talo. Also: [llms.txt](https://talopartnerhub.vercel.app/llms.txt), [llms-full.txt](https://talopartnerhub.vercel.app/llms-full.txt), [OpenAPI](https://talopartnerhub.vercel.app/openapi.json), [MCP server card](https://talopartnerhub.vercel.app/.well-known/mcp.json). Every human page under https://talopartnerhub.vercel.app/docs also answers `Accept: text/markdown` with its Markdown. --- Human page: https://talopartnerhub.vercel.app/docs/agents · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Quickstart > From nothing to a paid sandbox payment in five commands. ```bash npx talo-hub login # the person authorizes Talo sandbox in the browser; exits by itself when they do npx talo-hub keys create --name my-shop --scope charge --dotenv .env.local # the server's key, written straight to the file npx talo-hub payments create --amount 15000 --external-id order-1001 npx talo-hub sandbox pay npx talo-hub events list ``` The payment answer has `id`, `status` (PENDING), `cvu`, `alias`, `amount`, `paid` and `refunded` (numbers, 0 at first) and `expires_at`. Show the buyer the CVU or alias with the exact amount. Creating it emits `payment.created`; after the simulated transfer the status is SUCCESS and the events are `payment.updated` and `payment.paid` (`events list --payment ` shows only that payment's). With MCP instead of the CLI: create_payment, simulate_transfer, get_payment, list_events. To show the payment to a buyer: send them to its `payment_url` (Talo's own page), or render the `` widget on your page with the payment's `id` and `checkout_token` (a read-only token for that payment, also in the answer). To sell without a checkout, create a payment link: `npx talo-hub links create --title "Clase de yoga" --amount 15000`. See [checkout](https://talopartnerhub.vercel.app/docs/checkout.md). --- Human page: https://talopartnerhub.vercel.app/docs · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Sandbox: test everything yourself > Simulated transfers, every payment status, webhook pings, and the end-to-end test plan. Sandbox keys start with `tph_test_`. Nothing moves real money, so run every test without asking. ## Simulate the buyer's transfer - MCP: `simulate_transfer { payment_id, amount? }` - CLI: `npx talo-hub sandbox pay [--amount ]` - API: `POST /api/v1/sandbox/transfers { "payment_id": "…", "amount": 15000 }` Transfers to a payment can be simulated as many times as you want; each adds to `paid`. | Transfer | Status | Events | |---|---|---| | exact amount (default) | SUCCESS | payment.updated, payment.paid | | less | UNDERPAID | payment.updated | | more | OVERPAID | payment.updated, payment.paid | | nothing until it expires | EXPIRED | payment.expired (when Talo reports it). Payments expire after the account's cancellation period (PATCH /api/v1/account `cancellation_period_minutes`, 5 minimum, scope full); there is no command to expire one on demand. | Talo credits it in a few seconds; poll get_payment or list_events. To see the whole flow before writing code, use the [demo shop](https://talopartnerhub.vercel.app/demo): it is a merchant of the hub like any other. Its order page shows the exact `sandbox pay` command for each payment. ## End-to-end test plan 1. Create a payment with an external_id from your system. 2. Simulate an UNDERPAID transfer (amount - 1), check your code does not fulfil the order. 3. Simulate the rest, check it becomes SUCCESS and your webhook handler fulfils it once. 4. Repeat the same webhook (or the same event id) and check nothing is fulfilled twice. 5. Refund it (needs scope full) and check the refund shows in get_payment. 6. Register your webhook and send a ping: `POST /api/v1/webhook-endpoints/{id}/test` (MCP test_webhook, CLI `webhooks test `). It must answer 2xx after verifying the signature. This needs a public https URL that answers without a redirect or a login: a production domain, a tunnel, or a preview opened on purpose (Vercel protects previews and generated *.vercel.app URLs by default; see [talo-stack-nextjs-vercel](https://talopartnerhub.vercel.app/skills/talo-stack-nextjs-vercel/SKILL.md)). Forwarded local events are unsigned and don't count for readiness. 7. `check_go_live` / `GET /api/v1/readiness` should report ready. ## Local webhooks `npx talo-hub events listen --forward-to http://localhost:3000/api/webhooks/talo` relays events to localhost. Those relayed events are **not signed** and carry `talo-hub-forwarded: 1`: accept them only in development. With `--json` it prints one event per line; the last few existing events come first with `"replayed": true`. --- Human page: https://talopartnerhub.vercel.app/docs/sandbox · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Credentials: sandbox and production, safely > Key types and scopes, where to keep them, least privilege, rotation, and what never to do. ## Keys | Prefix | Environment | |---|---| | `tph_test_` | Talo sandbox: test money | | `tph_live_` | Talo production: real money | Each key also has a **scope**: | Scope | Can | Use it for | |---|---|---| | `read` | read payments, payment links, events, the account and its settings, balance and movements (when Talo allows them), keys and webhook URLs, readiness | dashboards, support agents, reports | | `charge` | read + create payments + create and disable payment links + sandbox transfers + webhook pings | a checkout server, an agent that charges | | `full` | everything, including refunds, price changes, keys and webhooks | the account owner, admin tasks | Only a `full` key (or the dashboard) can create keys, of any scope; new keys default to `charge`. Requests outside the scope fail with `403 insufficient_scope`. The key `npx talo-hub login` stores is `full` (it's the person's own); registering webhooks and creating keys need `full`. `TALO_HUB_API_KEY` is both the name your server reads and the CLI's override: if you export a project's `charge` key in your shell, the CLI uses it instead of your login, and commands that need `full` fail. Keep project keys in the project's env file only. ## Getting keys without exposing them - **MCP with OAuth (best for agents):** add `https://talopartnerhub.vercel.app/api/mcp` with no header. The client runs OAuth in the browser; the person picks sandbox or production and the scope; the key goes straight to the client and never appears in the conversation. - **CLI:** `npx talo-hub login` stores a personal key in the user's config (mode 600). `npx talo-hub keys create --name --scope charge` creates a project key; add `--dotenv .env.local` to write it into that file, or `--vercel production|preview[:]` to store it as a Vercel Secret, so it never appears on screen. `webhooks add` takes the same flags for `TALO_HUB_WEBHOOK_SECRET`. Without them the value is printed once. - **Dashboard:** API keys → Create. ## Rules 1. Keep keys in server-side environment variables or a secret manager (`TALO_HUB_API_KEY`), never in client code, the repo, logs, issues or chat messages. Add `.env*` to .gitignore. A payment's `checkout_token` (`ct_…`) is not a key: it only reads that payment's public view, so it may go to the browser for the widget. 2. One key per project and environment, named after it, so it can be revoked alone. 3. Use `charge` for checkout servers and agents; keep `full` for people. 4. Sandbox and production keys go in different environments of the deployment (for example Vercel Preview vs Production), never mixed. 5. Rotate: create the new key, deploy it, then `npx talo-hub keys revoke ` (or DELETE /api/v1/api-keys/{id}). Revoke at once if a key leaks. 6. Webhook secrets (`whsec_…`) are shown once: store them like keys (`TALO_HUB_WEBHOOK_SECRET`). 7. Before any production refund, price change or new production key, confirm with the person: say the payment, the amount and what will happen. ## In CI `TALO_HUB_API_KEY=tph_test_… npx talo-hub … --json`, with the key as a CI secret. Use sandbox keys in CI; production only in the deploy that serves real buyers. --- Human page: https://talopartnerhub.vercel.app/docs/credentials · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Security: what the hub stores and how to revoke it > Who operates the hub, what it keeps (secrets encrypted), what each key can do, and how to revoke one key or disconnect the whole account. ## Who operates it Talo Partner Hub is run by an official partner of Talo Pay, part of [Talo's partner program](https://talo.com.ar/partner); it isn't a Talo product. It works with partner credentials Talo issued (sandbox today; production once Talo issues the production credentials), and Talo processes every payment in the merchant's own account. You can confirm the partnership directly with Talo through its official WhatsApp line, [+54 11 3371-1752](https://wa.me/541133711752), the one Talo lists in its terms of service (https://talo.com.ar/terms-of-service, under Información de contacto). ## Where the money is Payments are created and processed by Talo in the merchant's own Talo account: buyers transfer to a CVU of that account, and the money stays there. The hub never holds funds, and its API can't send money to an account of its choosing: the only money that leaves the merchant's account through it is a refund of a payment (`full` keys only), which Talo sends back for that payment; the hub can't set where it goes. Otherwise it creates and reads payments and payment links, simulates transfers in sandbox and adjusts the account's payment settings. ## What the hub stores | What | How | |---|---| | The account's Talo authorization (a partner token Talo issues when the person authorizes the hub) | Encrypted with AES-256-GCM. The key lives in the hub's deployment settings, never in the database, and each value is bound to its purpose. | | Hub API keys (`tph_test_…`, `tph_live_…`) | Only a SHA-256 hash and a short prefix. A new key on its way to the CLI or an MCP client is kept encrypted until that client picks it up (minutes; if nobody does, the hub revokes it); after that it can't be read back. | | Webhook secrets (`whsec_…`) | Encrypted like the Talo authorization; shown once. | | Payments the hub created | Talo's payment id, your external_id, amount, status and the payment's events (status, CVU, alias, amounts, refunds, motive and payment link). The buyer's `client_data` is passed to Talo and not kept. | | Payment links | Title, description, amount or range, redirect URL, and when it was created and disabled. A payment a buyer starts from a link records which link it came from. For the limits only, each new payment attempt from a link leaves a keyed hash (HMAC-SHA256, 32 hex characters) of the link and the buyer's IP in a table of its own, for a day (older entries are deleted on the link's next attempt); the payment doesn't carry it, and the IP itself is never stored. | | Checkout tokens (`ct_…`) | Not stored: each is signed (HMAC-SHA256 with its own key) and checked when used. | | Webhook URLs and delivery results | As registered. A Vercel protection-bypass secret in the URL is stored too, but never shown back. | | Usage | Which key called which route, the channel and the status code, for the account's metrics. No request bodies, no keys. | The CLI keeps the person's login key in their user config, readable only by them (mode 600 on macOS and Linux; the user profile on Windows), and there too, while they authorize, the pending login so it can be picked up again: it includes a single-use token that collects that login's key. The CLI deletes it when the login connects or fails for a reason other than expiry, on logout or disconnect, and once the hub no longer honors it (about 20 minutes after the login started) the next time it saves its config. With MCP over OAuth the browser only carries a one-time code; the MCP client trades it (with PKCE) for the key at the hub's token endpoint, so the key never goes through the conversation. Dashboard sessions are encrypted, HttpOnly cookies, and the API only accepts them together with a header a cross-site page can't send. ## What each key can do | Scope | Can | Can't | |---|---|---| | `read` | read payments, payment links, events, the account and its settings, balance and bank movements (when Talo allows them), the list of keys and webhook URLs, readiness | create or change anything | | `charge` | read, create payments, create and disable payment links, simulate sandbox transfers, ping webhooks | refund, change amounts or expiry, manage keys or webhooks | | `full` | everything above, plus refunds, price and expiry changes, keys, webhooks, account settings, disconnecting | | Only a `full` key (or the dashboard) can create keys, of any scope; `read` and `charge` keys can't create keys. Checkout servers and agents that charge get `charge`: a leaked `charge` key can create payments but can't move money out. Sandbox (`tph_test_`) and production (`tph_live_`) are separate keys on separate Talo accounts. Agents following the hub's skills confirm with the person before any production refund, price change or new production key. ## Revoking | To | Do | |---|---| | Revoke one key | `npx talo-hub keys revoke `, the dashboard, or `DELETE /api/v1/api-keys/{id}` | | Sign the CLI out | `npx talo-hub logout` (`--all` for both environments): revokes its key on the hub (if the hub can't be reached, it says so, to revoke it from the dashboard) and deletes it from the machine | | Stop a webhook | `npx talo-hub webhooks remove ` | | Disconnect the account from the hub | the dashboard's **Desconectar**, `npx talo-hub disconnect --yes` (production needs `--production` too, even when it's the active environment), or `DELETE /api/v1/account` with a `full` key. The hub erases its copy of the Talo authorization, revokes every key, disables every webhook endpoint and ends every dashboard session of that environment, all at once. Payments and money stay in Talo. Coming back is a new authorization (`npx talo-hub login`), and sessions from before the disconnect stay ended. | Disconnecting doesn't revoke the authorization on Talo's side, and the hub keeps its record of past payments and events (no buyer data). It's the account owner's decision: an agent must not run it unless the person asks for it in the conversation. Disconnecting also disables every payment link of the account for good, and its checkout tokens stop working. ## Checkout token and payment links - **Checkout token** (`checkout_token`, `ct_…`): comes with each payment the API creates or reads, so the merchant's page can show that payment with the widget. It is read-only and names one payment of one account: it opens that payment's public view (`GET /api/public/payments/{id}`: status, amounts, CVU, alias, expiry, motive, Talo's payment page) and nothing else, no other payment, no writes, no account data. It ends 15 days after it was issued. It can't be revoked one by one: disconnecting the account stops them all, and tokens issued before a disconnect stay dead after the account reconnects. A bad, expired or another payment's token gets the same 404 as a payment that doesn't exist, so the endpoint doesn't reveal what exists. - **Payment links** (`/l/`): anyone with the URL can start a payment, so the hub allows up to 20 payments per hour from the same network (IP) per link, and a breaker of 200 per link per 10 minutes; a buyer who comes back to the link gets the payment they already started (also after a partial transfer), which doesn't count. Buyers on the same network (one Wi-Fi, one office) count together. The network is told apart by a keyed hash of the link and the IP, which lives only in the limits' own table, for a day; the IP itself is never stored. Coming back works with an HttpOnly cookie scoped to that link's path, for a day, while that payment is still PENDING or UNDERPAID for the same amount, so the buyer doesn't get a new CVU. A disabled link, or one whose account was disconnected, says only that it isn't available. - Both are only UI. Fulfil orders on the signed webhook, never on the widget's `talo-paid` event or a buyer returning to your site: anyone can fire a browser event. ## Webhooks Every delivery is signed (HMAC-SHA256 over the timestamp and the raw body), and your verifier accepts it only within 5 minutes of that timestamp, so your server can tell it came from the hub; see [webhooks](https://talopartnerhub.vercel.app/docs/webhooks.md). The hub doesn't follow redirects, and it never shows back a Vercel protection-bypass secret in a webhook URL. --- Human page: https://talopartnerhub.vercel.app/docs/security · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # From sandbox to production > The readiness checklist and the exact steps to start charging real money. ## 1. Finish sandbox Run `check_go_live` (MCP), `npx talo-hub doctor` or `GET /api/v1/readiness` with the sandbox key. Each failed check says how to fix it. Ready means: active account, a payment created and paid, a webhook registered that accepted a signed event, and no failing deliveries. ## 2. Connect production - The person runs `npx talo-hub login --production`, or authorizes the MCP client again choosing **Producción**. Talo may take up to 36 h to activate a new account (account_status PENDING); no CVUs until then. - Create the server key: `npx talo-hub keys create --production --name --scope charge` (on Vercel add `--vercel production`). Put it in the production environment only. ## 3. Production webhook - Register the production URL: `npx talo-hub webhooks add https:///api/webhooks/talo --production` (on Vercel add `--vercel production`) and store the new `whsec_` secret in production. - Ping it: `npx talo-hub webhooks test --production`. It must answer 2xx. ## 4. One real payment Create a small payment and have the person transfer it from their bank. Check it becomes SUCCESS, the webhook fulfils the order, and `readiness` with the production key reports ready. ## 5. After launch - Watch `list_events` / the dashboard for UNDERPAID payments and failed deliveries. - Refunds in production need scope `full` and the person's confirmation. --- Human page: https://talopartnerhub.vercel.app/docs/go-live · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # MCP server > Connect with OAuth or a key, and every tool with its scope. URL: `https://talopartnerhub.vercel.app/api/mcp` (Streamable HTTP). ## Connect - **OAuth (recommended):** `claude mcp add --transport http talo https://talopartnerhub.vercel.app/api/mcp`, then authenticate (in Claude Code, `/mcp`). The browser opens; the person chooses sandbox or production and the scope. Discovery: `https://talopartnerhub.vercel.app/.well-known/oauth-protected-resource`, dynamic client registration and Client ID Metadata Documents are supported, PKCE S256 required. - **Key:** send `Authorization: Bearer tph_…`. `npx talo-hub mcp [--client claude|cursor|desktop]` prints the OAuth config; add `--with-key` for a key-based one. ## Tools The server only lists the tools the key's scope allows. - `create_payment`: Crea un pago y devuelve el CVU, el alias y el checkout_token para el widget. - `get_payment`: Estado y montos de un pago. - `list_payments`: Pagos de un período. - `create_payment_link`: Crea un link de pago reutilizable, con su URL y su QR. - `list_payment_links`: Tus links de pago. - `get_payment_link`: Un link de pago y los pagos que se crearon con él. - `disable_payment_link`: Desactiva un link de pago para siempre. - `simulate_transfer` (sandbox): Paga un pago de sandbox con una transferencia simulada. - `refund_payment`: Reembolso total o parcial. - `update_payment_price`: Cambia el monto de un pago. - `extend_payment`: Extiende el vencimiento de un pago pendiente. - `get_account`: Estado de la cuenta Talo y permisos de la key. - `get_balance`: Saldo de la cuenta (cuando Talo lo habilite para partners). - `list_transactions`: Movimientos de la cuenta (cuando Talo lo habilite para partners). - `list_events`: Eventos del hub (payment.paid, payment.updated…). - `list_webhook_endpoints`: Webhooks registrados. - `create_webhook_endpoint`: Registra un webhook y devuelve su secreto una vez. - `test_webhook`: Manda un evento de prueba firmado a tu webhook. - `create_api_key`: Crea una key para un proyecto, con el permiso justo. - `check_go_live`: Checklist para pasar a producción. - `read_docs`: Lee cualquier página de esta documentación en Markdown. The server is stateless (no session id) and answers over SSE (`data:` lines). Every tool result is JSON, both as text and as `structuredContent`; failures come with `isError: true` and `{ "error": { "code", "message", "details"? } }`. Use read_docs to read any of these pages from inside the conversation. --- Human page: https://talopartnerhub.vercel.app/docs/mcp · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # CLI > talo-hub commands, environments and non-interactive use. `npx talo-hub ` (Node 20+). `--json` for machine output (JSON on stdout, human messages on stderr), `--production` / `--sandbox` to pick the account, `TALO_HUB_API_KEY` to use a key without login. Exit codes: 0 success; 1 any failure, including `doctor` when not ready and `webhooks test` when the endpoint didn't answer 2xx. With `--json`, failures print `{ "error": { "code", "message", "status" } }`. ## Login from an agent `npx talo-hub login` waits until the person authorizes and then exits by itself (0 = connected): that exit is your signal, so don't ask them whether they authorized. Show them the link and code it prints; they compare the code in their browser. If your tool notifies you when a background command exits (Claude Code's background commands do), run it in the background and continue when that notice arrives; otherwise run it in the foreground with a timeout of at least 10 minutes. If it gets cut off, run the same command again: it picks up the same login, with the same link and code. If it expired, running it again checks once whether the person finished in Talo anyway (and collects the key), and otherwise starts a new login with a new link and code. With `--json`, stderr carries one line `{"event":"waiting_for_authorization","login_url","user_code","expires_at","resumed"}` as soon as the login waits (again, with the new link and code, if it had to start over: use the last one), and stdout gets the result when it ends. The link and code last 10 minutes. `TALO_HUB_NO_BROWSER=1` skips opening the browser (the link is printed anyway). - `talo-hub login [--production]`: Conecta tu cuenta Talo desde el navegador (sandbox por defecto). Termina solo cuando autorizás; si se cortó, correrlo de nuevo retoma el mismo login. - `talo-hub logout [--production | --all]`: Revoca la key guardada en el hub y la borra de tu máquina. - `talo-hub disconnect --yes [--production]`: Desconecta tu cuenta del hub: borra su copia de la autorización de Talo y revoca todas las keys y webhooks. Producción pide --production. - `talo-hub use `: Elige el entorno de los próximos comandos. - `talo-hub status`: Cuenta conectada, su estado y los permisos de la key. - `talo-hub doctor`: Checklist para pasar a producción, con lo que falta (sale con 1 si falta algo). - `talo-hub payments create --amount 15000 [--external-id] [--motive]`: Crea un pago y muestra el CVU, el alias y el snippet del widget para tu página. - `talo-hub payments get `: Estado de un pago. - `talo-hub payments list [--from] [--to]`: Pagos de un período (30 días por defecto). - `talo-hub payments refund [--amount]`: Reembolso total o parcial. - `talo-hub sandbox pay [--amount]`: Simula la transferencia del comprador. - `talo-hub links create --title (--amount | --min --max ) [--description] [--redirect-url]`: Crea un link de pago reutilizable (precio fijo, o un rango en el que elige el comprador) y muestra su URL y la de su QR. - `talo-hub links list | get | disable `: Tus links de pago, uno con los pagos que se crearon con él, o desactivarlo para siempre. - `talo-hub events list [--payment ] [--type ] [--after ] [--limit]`: Eventos recientes, filtrables por cobro o tipo. - `talo-hub events listen [--forward-to ]`: Recibe eventos en vivo y los reenvía a localhost, sin túneles. - `talo-hub webhooks list | add [--vercel | --dotenv ] | remove | test `: Webhooks firmados: registrar (probando antes la URL), quitar y mandar un evento de prueba. - `talo-hub keys list | create --name [--scope] [--vercel | --dotenv ] | revoke `: Keys para tus proyectos (scope charge por defecto), guardadas en Vercel o en tu .env sin mostrarlas. - `talo-hub balance, transactions [--from] [--to] [--limit] [--cursor]`: Saldo y movimientos (cuando Talo los habilite para partners). - `talo-hub mcp [--client claude|cursor|desktop] [--with-key]`: Configuración del MCP lista para pegar: OAuth por defecto, o con tu key. - `talo-hub skills list | install [--stack ] [--global]`: Instala las skills de Talo en .claude/skills, con el kit del stack del proyecto. --- Human page: https://talopartnerhub.vercel.app/docs/cli · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # REST API > Every endpoint with its fields, the payment object, errors and payment statuses. Base URL: https://talopartnerhub.vercel.app. Auth: `Authorization: Bearer tph_…`. JSON in and out; every answer is a JSON object at the top level (no wrapper). OpenAPI with schemas: https://talopartnerhub.vercel.app/openapi.json. ## The payment object Returned by create, get and list (`{ payments: [...] }`), and inside webhook events as `data.payment`: | Field | Type | | |---|---|---| | `id` | string | Talo's payment id, e.g. `PAR-talo-hub-` | | `status` | string | PENDING, SUCCESS, OVERPAID, UNDERPAID, EXPIRED | | `external_id` | string | Your order id | | `amount` | number | ARS asked for | | `currency` | string | `ARS` | | `cvu`, `alias` | string | Where the buyer transfers | | `paid`, `refunded` | number | ARS received and refunded; 0 when none | | `refunds` | array | `{ id, status: PENDING\|SUCCESS\|FAILED, type: FULL\|PARTIAL, amount: number, currency, created_at }` | | `expires_at`, `created_at` | string | ISO 8601 (created_at may be missing on create) | | `motive` | string | The concept you sent | | `payment_url` | string | Talo's hosted page for the payment, when Talo provides one (`redirect_url` is where it sends the buyer back) | | `checkout_token` | string | Only in the answers to create and get (not in lists or webhook events): a read-only token (`ct_…`) for this payment's public view, valid 15 days from when it was issued. Safe in the browser, for the widget; see [checkout](https://talopartnerhub.vercel.app/docs/checkout.md) | Rules: - `amount`: ARS, greater than 0, up to 2 decimals. - `external_id`: up to 100 characters; unique per account (sandbox and production are different accounts). Creating a payment with an external_id that already has one returns **that existing payment**, whatever its amount or status. So after EXPIRED, create the new payment with a **new** external_id (e.g. `order-1001-2`). - Statuses only move forward with transfers: PENDING → UNDERPAID → SUCCESS/OVERPAID, or → EXPIRED. Reading a payment also brings its events up to date. ## Polling There is no published rate limit. Poll a payment every 3 to 10 seconds while a buyer is on the page, and stop when it is SUCCESS, OVERPAID or EXPIRED. Talo credits sandbox transfers in about 10 to 25 seconds. From the browser, poll the public view with the payment's checkout_token (`GET /api/public/payments/{id}?token=…`), never with your key; the `` widget does it for you ([checkout](https://talopartnerhub.vercel.app/docs/checkout.md)). ## Errors `{ "error": { "code", "message", "details"? } }` | Code | HTTP | Meaning | |---|---|---| | unauthorized | 401 | Missing, invalid or revoked key | | insufficient_scope | 403 | The key's scope doesn't allow it (read < charge < full) | | sandbox_only | 403 | Only with a tph_test_ key | | payment_not_found | 404 | No payment with that id in this account | | not_found | 404 | Unknown webhook endpoint or key id; on the public payment view, no payment for that checkout_token (bad, expired or another payment's: it doesn't say which) | | link_not_found | 404 | No payment link with that id in this account | | link_disabled | 410 | The payment link was already disabled | | invalid_amount, invalid_field, invalid_date, invalid_json, invalid_url, invalid_scope, invalid_cursor, invalid_title, invalid_description, invalid_redirect_url | 422 | The request is malformed; the message says which field | | talo_rejected | 400/404/409/422 | Talo refused it; its reason is in details.talo_message (e.g. refund.already.pending) | | talo_reconnect_required | 409 | Talo no longer accepts the account's authorization: the person runs `npx talo-hub login` (plus `--production` for a tph_live_ key) or re-authenticates the MCP server | | talo_partner_unsupported | 501 | Talo doesn't offer balance and movements to partner integrations yet; payments always work | | talo_unavailable | 502 | Talo didn't answer properly or in time. It may have done it anyway: read the payment before retrying (creating a payment again with the same external_id is safe); then retry with backoff | | not_configured | 503 | The hub is not set up for that environment yet | A new account Talo hasn't activated yet (status PENDING, up to 36 h) can't create CVUs: payments fail with talo_rejected until it is ACTIVE. ## Payment statuses - PENDING: Esperando la transferencia. - SUCCESS: Pagado exacto. - OVERPAID: Pagó de más. - UNDERPAID: Pagó de menos: no entregues, pedí el resto. - EXPIRED: Venció sin pagarse. ## Endpoints ### GET /api/v1/account (scope read) Cuenta Talo conectada: estado de activación y, cuando Talo los informa, prefijo de alias y vencimiento de pagos. ### PATCH /api/v1/account (scope full) Configura la cuenta. Solo cambian los campos enviados. - `alias_prefix` (body, string): Hasta 8 caracteres: minúsculas, números y guiones bajos. - `cancellation_period_minutes` (body, number): Vencimiento de los pagos nuevos, de 5 a 20160 minutos. - `automatic_cancellation` (body, boolean): Cancela solos los pagos vencidos. ### DELETE /api/v1/account (scope full) Desconecta la cuenta del hub, todo junto: borra la copia de la autorización de Talo, revoca todas las keys, desactiva los webhooks y cierra las sesiones del dashboard. Los pagos y la plata siguen en Talo, y el hub conserva su registro de pagos y eventos pasados. Para volver, autorizá de nuevo (npx talo-hub login). Es para la persona dueña de la cuenta: un agente no debería llamarlo sin que se lo pida. ### POST /api/v1/payments (scope charge) Crea un pago por transferencia y devuelve el CVU y el alias para pagarlo. Repetir el mismo external_id devuelve el pago existente: los reintentos nunca cobran dos veces. La respuesta trae payment_url (la página de Talo del pago) y checkout_token, un token de solo lectura de ese pago para el widget (ver Checkout). - `amount` (body, number, required): Monto en ARS. - `external_id` (body, string): Tu ID de orden. Hace seguros los reintentos. - `motive` (body, string): Concepto que ve el comprador. - `redirect_url` (body, string): Adónde vuelve el comprador desde la página de pago de Talo. - `client_data` (body, object): first_name, last_name, email, phone, dni, cuit. ### GET /api/v1/payments (scope read) Pagos de un período (por defecto, los últimos 30 días). Talo no les deja listar pagos a las integraciones de partners: entonces el hub devuelve los que se crearon a través de él, con el último estado que registró, y `source: "hub"` (con `source: "talo"`, la lista viene de Talo). GET /api/v1/payments/{id} trae el detalle completo. - `from` (query, string): Fecha ISO 8601. - `to` (query, string): Fecha ISO 8601. ### GET /api/v1/payments/{id} (scope read) Estado, montos pagados y reembolsados, y reembolsos de un pago. paid y refunded son números (0 si no hay). refunds: [{ id, status: PENDING|SUCCESS|FAILED, type: FULL|PARTIAL, amount, currency, created_at }]. Leer un pago también pone al día sus eventos y trae un checkout_token nuevo (15 días). ### PUT /api/v1/payments/{id}/price (scope full) Cambia el monto. Talo recalcula el estado de un pago ya pagado. - `amount` (body, number, required): Nuevo monto en ARS. ### PUT /api/v1/payments/{id}/expiration (scope full) Extiende el vencimiento de un pago PENDING. - `expires_at` (body, string, required): Fecha ISO 8601. ### POST /api/v1/payments/{id}/refunds (scope full) Reembolsa un pago, total o parcial. Asincrónico: empieza PENDING. - `amount` (body, number): ARS para un reembolso parcial. Sin amount, reembolso total. ### POST /api/v1/payment-links (scope charge) Crea un link de pago reutilizable (/l/): cada comprador que toca Pagar recibe un pago nuevo y va a la página de Talo con el CVU y el alias. Mandá amount (precio fijo) o min_amount y max_amount (el comprador escribe el monto). Devuelve url y qr_url. Sus pagos llevan external_id link__ (el id tiene 12 letras y números: external_id.slice(5, 17)) y motive = title, y el pedido se entrega con el webhook firmado, como cualquier pago. Hasta 20 pagos por hora desde una misma red (IP) por link, y un freno de 200 por link cada 10 minutos; quien vuelve al link recupera el pago que ya había empezado (también si transfirió de menos), y eso no cuenta. - `title` (body, string, required): Qué se paga, de 1 a 80 caracteres. Lo ve el comprador y es el motive de cada pago. - `description` (body, string): Hasta 280 caracteres. - `amount` (body, number): Precio fijo en ARS: positivo, hasta 2 decimales y 100.000.000. - `min_amount` (body, number): Con max_amount y sin amount: el comprador escribe el monto, dentro de ese rango. - `max_amount` (body, number): El monto más alto que puede escribir el comprador. - `redirect_url` (body, string): URL https adonde la página de Talo devuelve al comprador (http solo para localhost). ### GET /api/v1/payment-links (scope read) Tus links de pago, del más nuevo al más viejo (hasta 100), con los desactivados (active: false). ### GET /api/v1/payment-links/{id} (scope read) Un link de pago y los pagos que se crearon con él: payments [{ id, status, amount, external_id, created_at }], del más nuevo al más viejo. ### DELETE /api/v1/payment-links/{id} (scope charge) Desactiva un link de pago para siempre. Los pagos que ya empezaron con él se pueden seguir pagando. No se puede reactivar: creá otro. Un link que ya estaba desactivado responde 410 link_disabled. ### GET /api/public/payments/{id} (no key: the payment's checkout_token) La vista pública de un pago, para el widget o tu propia página: estado, monto, pagado, CVU, alias, vencimiento y payment_url. Sin API key: la autoriza el checkout_token del pago (lo devuelven POST /api/v1/payments y GET /api/v1/payments/{id}), que solo sirve para leer ese pago y vence a los 15 días. Cualquier origen puede llamarla (CORS *) y el CDN guarda cada respuesta 3 segundos. Un token inválido, vencido o de otro pago, una cuenta desconectada y un pago que no existe responden lo mismo: 404 not_found. - `token` (query, string, required): El checkout_token del pago (ct_…). ### POST /api/v1/sandbox/transfers (scope charge, sandbox only) Simula la transferencia del comprador a un pago de sandbox. Con menos monto probás UNDERPAID; con más, OVERPAID. Dispara los mismos eventos que una transferencia real. - `payment_id` (body, string, required): El pago a pagar. - `amount` (body, number): ARS. Por defecto, el monto del pago. ### GET /api/v1/balance (scope read) Saldo de la cuenta. Talo todavía no lo habilita para integraciones de partner: hoy responde talo_partner_unsupported (501). ### GET /api/v1/transactions (scope read) Transferencias entrantes y salientes, paginadas. Talo todavía no lo habilita para integraciones de partner: hoy responde talo_partner_unsupported (501). El estado de cada cobro siempre está en /api/v1/payments. - `from` (query, string): Fecha ISO 8601. - `to` (query, string): Fecha ISO 8601. - `limit` (query, number): Hasta 200. - `cursor` (query, string): El next_cursor de la página anterior. ### GET /api/v1/events (scope read) Eventos de la cuenta, para escuchar sin exponer un servidor. Con after: los posteriores a ese ID, del más viejo al más nuevo (seguí con next_after). Con recent: los últimos n, del más nuevo al más viejo. - `after` (query, number): Devuelve los eventos posteriores a este ID. - `recent` (query, number): Los últimos n eventos (1 a 200). - `limit` (query, number): Hasta 200. - `payment_id` (query, string): Solo los eventos de ese cobro. - `type` (query, string): Solo ese tipo, p. ej. payment.paid. ### GET /api/v1/webhook-endpoints (scope read) Tus webhooks. ### POST /api/v1/webhook-endpoints (scope full) Registra un webhook https. Devuelve el secreto de firma una sola vez. - `url` (body, string, required): URL https que recibe los eventos. ### DELETE /api/v1/webhook-endpoints/{id} (scope full) Da de baja un webhook. ### POST /api/v1/webhook-endpoints/{id}/test (scope charge) Manda ya un evento webhook.test firmado y dice si tu endpoint respondió 2xx. No mueve plata: sirve en sandbox y en producción para verificar tu handler. ### GET /api/v1/readiness (scope read) Checklist para pasar a producción: qué está hecho y cómo resolver lo que falta. ### GET /api/v1/api-keys (scope read) Tus API keys (solo el prefijo). ### POST /api/v1/api-keys (scope full) Crea una API key. Se muestra una sola vez. Solo con una key full (o desde el dashboard). La nueva puede tener cualquier scope y es charge si no pedís otro. - `name` (body, string): Para reconocerla después. - `scope` (body, string): read, charge o full (por defecto charge). ### DELETE /api/v1/api-keys/{id} (scope full) Revoca una API key. --- Human page: https://talopartnerhub.vercel.app/docs/api · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Webhooks > Events, the exact signature format, verification code, retries, ordering and a test vector. Register: `POST /api/v1/webhook-endpoints { "url": "https://…" }` (scope full; the CLI's login key has it) → `{ id, url, secret }`, with `secret` (whsec_…) shown once. Ping it any time: `POST /api/v1/webhook-endpoints/{id}/test` (MCP test_webhook, CLI `webhooks test `). To change the secret, register the URL again and remove the old endpoint once you deploy the new secret. The signed path needs a public **https** URL (a deployment or a tunnel). `talo-hub events listen --forward-to` is for local development and does not sign. Register the **final** URL: the hub doesn't follow redirects, so a trailing slash, an apex domain that redirects to www, or http all fail. `npx talo-hub webhooks add` first sends one unsigned POST and refuses a URL that redirects, is behind Vercel Deployment Protection, or accepts an unsigned event (a correct handler answers 400). A failed ping answers `{ delivered: false, status_code, error, reason }` with `reason` one of `redirect`, `vercel_protection`, `timeout`, `network`, `http_error`, and `error` saying what to fix. A `x-vercel-protection-bypass` value in the URL is never shown back. ## The request `POST ` with headers: - `content-type: application/json` - `talo-hub-event: ` - `talo-hub-signature: t=,v1=<64 lowercase hex chars>` (exactly one t and one v1, comma-separated, no spaces) `v1 = hex(HMAC-SHA256(secret, "."))`. Verify on the raw body bytes, and accept only when t is within 5 minutes of now (past or future). Body: `{ "id": , "type", "created_at", "data" }`. For payment events `data` is `{ "payment": , "previous_status" }`; for `webhook.test` it is `{ "message", "environment" }`; for `account.updated` it is `{ "account_status" }`. Event ids are integers, increasing per hub. - `payment.created`: Se creó un pago. - `payment.paid`: El pago quedó SUCCESS u OVERPAID: entregá el pedido. - `payment.updated`: Cambió el estado, el monto pagado o un reembolso. - `payment.expired`: El pago venció sin pagarse. - `account.updated`: Talo cambió el estado de la cuenta (por ejemplo, la activó). - `webhook.test`: Evento de prueba enviado con /test: confirmalo con 2xx y no hagas nada más. ## Answer and retries Answer 2xx within 10 seconds; anything else (or a timeout) is retried after 1 m, 5 m, 30 m, 2 h and 6 h, then given up. Answer 400 to a bad signature and 2xx to an event you already handled. Do slow work after answering. ## Ordering and repeats Deliveries are retried independently, so events can arrive out of order or twice. Handle each event `id` once, and before acting re-read the payment (`GET /api/v1/payments/{id}`) instead of trusting the event snapshot. `payment.paid` is emitted when a payment first becomes SUCCESS or OVERPAID; a later transfer only emits `payment.updated`. Fulfil orders from the webhook (keeping a "fulfilled" flag per order), and use polling only to update what the buyer sees. ## Verification (Node) ```ts import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyTaloHub(rawBody: string, header: string, secret: string): boolean { const parts = Object.fromEntries(header.split(",").map((p) => { const [k, ...v] = p.trim().split("="); return [k, v.join("=")]; })); const t = Number(parts.t); if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false; if (!/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false; const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest(); return timingSafeEqual(expected, Buffer.from(parts.v1, "hex")); } ``` ## Test vector To unit-test your verifier offline (with the 5-minute check disabled): - secret: `whsec_test_secret` - t: `1793000000` - raw body: `{"id":1,"type":"webhook.test","created_at":"2026-10-26T00:00:00.000Z","data":{"message":"Talo Partner Hub test event","environment":"sandbox"}}` - expected v1: `ee8e039a9e5e9ce3eac17705f1133bfd58776d46f5163e01ad6a42d2cc540b9e` ## Local development `npx talo-hub events listen --forward-to http://localhost:3000/api/webhooks/talo` POSTs each **new** event with the same JSON body and headers `content-type`, `talo-hub-event` and `talo-hub-forwarded: 1`, and **no signature**. The few existing events it prints at start (with `"replayed": true` under `--json`) are not forwarded. Accept forwarded events only in local development: `NODE_ENV === "development"` (Next.js `next dev`), or an explicit `TALO_HUB_DEV_FORWARD=1` plus a `tph_test_` key in other stacks; never when deployed. --- Human page: https://talopartnerhub.vercel.app/docs/webhooks · All pages: https://talopartnerhub.vercel.app/docs/index.md --- # Checkout: payment links and the transfer widget > Talo's payment page, reusable payment links (/l/, with a QR) and the widget or shadcn component, with the read-only checkout_token behind it. Three ways to show the buyer where to transfer, from least to most code. In all of them the order is fulfilled on the signed webhook (`payment.paid`, then re-read the payment; see [webhooks](https://talopartnerhub.vercel.app/docs/webhooks.md)): link pages and the widget are only UI, and anyone can fire a browser event. | You want | Use | Your code | |---|---|---| | To send the buyer somewhere to pay one order | the payment's `payment_url`: Talo's page with the amount, alias and CVU (copy buttons), a countdown, the account holder and CUIT | create the payment on the server, redirect to `payment_url` | | To sell without a checkout: a chat, a bio, a QR at the counter | a payment link, `/l/` | none: create the link once | | To keep the buyer on your own page | the `` widget, or its React twin from the shadcn registry | create the payment on the server, render the widget with its `id` and `checkout_token` | ## Payment links Create one (scope `charge`): - API: `POST /api/v1/payment-links { "title": "Clase de yoga", "amount": 15000 }`, or `"min_amount": 1000, "max_amount": 50000` instead of `amount` for an amount the buyer types. - CLI: `npx talo-hub links create --title "Clase de yoga" --amount 15000` (or `--min 1000 --max 50000`; also `--description`, `--redirect-url`). Then `links list`, `links get `, `links disable `. - MCP: create_payment_link, list_payment_links, get_payment_link, disable_payment_link. - Dashboard: Links de pago. Rules: `title` 1 to 80 characters after trimming (the buyer sees it; it is every payment's `motive`); `description` up to 280; either `amount` alone, or `min_amount` and `max_amount` with min ≤ max, all positive with up to 2 decimals and at most 100,000,000 ARS; `redirect_url` an absolute https URL (http only for localhost), passed to Talo as each payment's `redirect_url`. Errors: 422 `invalid_title`, `invalid_description`, `invalid_amount`, `invalid_redirect_url`; an unknown id or another account's link, 404 `link_not_found`. ```json { "id": "Ab3dE6gH9jKq", "url": "https://talopartnerhub.vercel.app/l/Ab3dE6gH9jKq", "qr_url": "https://talopartnerhub.vercel.app/l/Ab3dE6gH9jKq/qr.svg", "environment": "sandbox", "title": "Clase de yoga", "description": null, "amount": 15000, "min_amount": null, "max_amount": null, "redirect_url": null, "active": true, "created_at": "2026-10-29T15:00:00.000Z" } ``` - **The buyer** opens `url`: the title, the description, the amount (or a field for it, with the range), a notice on sandbox links that no real money moves, and a "Pagar por transferencia" button. Pressing it creates a payment (`external_id` `link__`, `motive` = title) and sends the buyer to its `payment_url` on Talo. Pressing it again from the same browser within a day, while that payment is still PENDING (or UNDERPAID, after a partial transfer) for the same amount, gives back the same payment instead of a new CVU. - **The QR**: `qr_url` is an SVG of `url` (cached for a day), ready to print. - **Limits**: up to 20 payments per hour from the same network (IP) per link, and a breaker of 200 per link per 10 minutes; a buyer who comes back to the link gets the payment they already started (also after a partial transfer), which doesn't count. Buyers on the same Wi-Fi count together. Past a limit the page asks the buyer to try again later. The network is told apart by a keyed hash of the link and the IP, kept for a day only for these limits; the IP is never stored. - **Fulfilment**: link payments emit the same events as any payment. On `payment.paid`, `data.payment.external_id` is `link__` and `motive` is the title: deliver what the link sells. Link ids are 12 letters and digits (`[0-9A-Za-z]`), so `external_id.slice(5, 17)` is always the link id, whatever the random part holds. `GET /api/v1/payment-links/{id}` lists the payments buyers started (id, status, amount, external_id, created_at), newest first. - **Disabling** (`DELETE /api/v1/payment-links/{id}`, scope `charge`) is for good: create a new link to sell again. Payments already started can still be paid. A link already disabled answers 410 `link_disabled`. Disconnecting the account disables all its links. A disabled or unavailable link shows the buyer "Este link de pago no está disponible", with no details. ## The transfer widget The server creates the payment (`POST /api/v1/payments` with an amount the server decides, never one from the browser) and hands the page only `id` and `checkout_token`: ```html ``` - Attributes: `payment-id` and `token` (required); `hub` (optional: the hub's base URL; by default the origin the script was loaded from). Changing one restarts it. - It shows the amount, alias and CVU with copy buttons and the expiry, and follows the status: PENDING (waiting for the transfer), UNDERPAID (what is still missing, in the amount field), SUCCESS or OVERPAID (paid), EXPIRED (don't transfer to these details), plus "not found" for a 404. Sandbox payments get a badge that no real money moves, and there is a link to `payment_url`. The texts are in Spanish (es-AR). - It reads the public view every 4 s, backs off up to 30 s on errors, pauses while the tab is hidden, and stops at SUCCESS, OVERPAID or EXPIRED, on a 404, or 15 minutes after `expires_at`. - Events, bubbling and composed: `talo-status` (detail: the public view, whenever status or paid change), `talo-paid`, `talo-expired`, `talo-error` (detail: `{ code, status, retrying }`). The element's `payment` property is the last view. Use them to update the page, never to fulfil. - Theme: CSS custom properties on the element or an ancestor: `--talo-accent`, `--talo-text`, `--talo-muted`, `--talo-bg`, `--talo-border`, `--talo-radius`, `--talo-font`. - Content-Security-Policy: add `https://talopartnerhub.vercel.app` (or the `hub` you set) to `script-src` (the script) and `connect-src` (its reads). `style-src` needs no change: the widget styles itself with a constructed stylesheet, not inline styles. Browsers without constructed stylesheets (Safari before 16.4) get a `