# Rapid Documentation > Rapid integration documentation, covering dispute prevention, chargeback alerts, transaction upload, webhooks and evidence capture. Página: https://doc.rapidchargeback.com/en/ ## With your AI assistant Connect the MCP and your assistant (Claude, Cursor, VS Code, ChatGPT) looks up this documentation while you code, with the real field names and error codes, instead of answering from memory. ```text https://doc.rapidchargeback.com/mcp/ ``` [How to set it up](https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/#mcp) · [Postman collection](https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/#postman-collection) · [llms.txt](https://doc.rapidchargeback.com/en/llms.txt) ## The products ### Prevention When the issuing bank opens an inquiry, Rapid answers with the order data so the dispute is never filed. [Overview](https://doc.rapidchargeback.com/en/produtos/prevencao/visao-geral/) · [Integration](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/) ### Alert The issuer flags the dispute before it becomes a chargeback, and you respond in time to refund or suspend access. [Overview](https://doc.rapidchargeback.com/en/produtos/alerta/visao-geral/) · [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/) ### Dispute Once the chargeback is open, Rapid builds and submits the defense within the card network's deadline. [Overview](https://doc.rapidchargeback.com/en/produtos/disputa/visao-geral/) ## Start here - [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/): The credentials, where to get them and how to send them on each channel. - [Send transactions](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/): The input channel that feeds Prevention and Dispute. - [Receive webhooks](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/): How Rapid delivers alerts and events to your system. - [Respond to an alert](https://doc.rapidchargeback.com/en/callback/visao-geral/): The status callback, with the accepted values and each provider's deadline. - [Capture evidence](https://doc.rapidchargeback.com/en/canais/capture/visao-geral/): The checkout snippet and the two event channels. - [Canonical examples](https://doc.rapidchargeback.com/en/referencia/exemplos-canonicos/): The fixed IDs and values used in every example here. --- # First integration > From zero to the first alert answered: credentials, webhook with validated signature, first transaction and the status callback, with ready-to-run commands. Página: https://doc.rapidchargeback.com/en/primeira-integracao/ The shortest path to a working integration. By the end of this guide your application receives alerts with a validated signature, sends transactions and answers alerts through the API. Each step points to the page with the full detail. > **Using an AI assistant?** Connect the [documentation MCP](https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/#mcp) before you start: the assistant then looks up these pages instead of guessing fields and codes. ## 1. Get the credentials In the Rapid dashboard, under **Settings › API**, generate the `client_id` and the `client_secret`. The secret is shown **only once**: copy it right away. If you lose it, generate it again in the same place (the previous credentials stop working at that same moment). Keep both in environment variables, never in the code: ```bash export RAPID_CLIENT_ID="your_client_id" export RAPID_CLIENT_SECRET="your_client_secret" ``` Details in [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ## 2. Confirm the credential works List your company's most recent alert: ```bash curl -u "$RAPID_CLIENT_ID:$RAPID_CLIENT_SECRET" \ "https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts?per_page=1" ``` | Response | What it means | |---|---| | `200` with `data` and `meta` | Correct credential. `data` may be empty if no alert has arrived yet | | `401 UNAUTHORIZED` | Wrong `client_id` or `client_secret` | | `403 COMPANY_BLOCKED` | The company is blocked at Rapid: contact support | ## 3. Register the webhook Under **Settings › Webhook**, register the URL that will receive the events. It must be **HTTPS** and a **public internet address**: the dashboard rejects `http://`, `localhost` and local network IPs. When you save, Rapid generates the **signing key** and shows it only once. Keep it in `RAPID_WEBHOOK_SECRET`. To receive on your machine during development, expose the local server with an HTTPS tunnel (see [Best practices](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/#use-a-public-internet-address)). ## 4. Validate the signature Every delivery carries `X-Webhook-Signature` and `X-Webhook-Timestamp`. Your endpoint checks the signature **before** processing anything. Copy the ready-made endpoint in your language (Node.js, Python or PHP) from [Webhook authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/#examples). All three follow the same rules: - the signature is computed over the **raw body**: do not parse it as JSON before validating - a delivery older than 5 minutes is rejected (replay protection) - any delivery that does not pass gets `401` Respond `2xx` quickly and process later, in a queue: Rapid waits up to 30 seconds and, with no response, tries again. Use the `alert_id` so you do not process the same alert twice. See [Best practices](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/). ## 5. Send the first transaction If you use **Prevention** or **Dispute**, they work with the sales transactions you send. Each transaction says which merchant (store, brand or seller) it belongs to, through the `merchant_id`. See yours: ```bash curl -u "$RAPID_CLIENT_ID:$RAPID_CLIENT_SECRET" \ "https://api.rapidchargeback.com/api/v1/merchants" ``` The `id` of each item in the response is the `merchant_id` (see [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/)). With it, the minimal transaction: ```bash curl -X POST https://api.rapidchargeback.com/api/v1/transactions \ -u "$RAPID_CLIENT_ID:$RAPID_CLIENT_SECRET" \ -H "Content-Type: application/json" \ -d '{ "merchant_id": "00000000-0000-0000-0000-000000000001", "external_id": "TXN-2026-001", "transaction_id": "cob_8f2a91c4", "transaction_date": "2026-04-01T15:30:00Z", "amount": 150.00, "currency": "USD", "card_bin": "424242", "card_last4": "4242", "items": [ { "product_description": "Assinatura Premium - Mensal", "unit_price": 150.00 } ] }' ``` - The `transaction_id` is the charge identifier in your payment gateway. It is how a future chargeback finds the transaction: without it the defense goes out with no history. - The response is `201` with the transaction `id` at Rapid: keep it. If you lose it, you can find the transaction [by the `external_id`](https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/#by-your-systems-id). - If the `warnings` key comes back, the transaction was created, but fields that improve protection are missing. All fields in [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/). For volume, use [batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/). ## 6. Answer the first alert When a **Mastercard** alert arrives, you have **24 hours** to answer. With the `alert_id` received in the webhook: ```bash curl -X PATCH https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts/00000000-0000-0000-0000-000000000099/status \ -u "$RAPID_CLIENT_ID:$RAPID_CLIENT_SECRET" \ -H "Content-Type: application/json" \ -d '{ "status": "account_suspended" }' ``` | Status | When to use | |---|---| | `notfound` | The transaction does not exist in your system | | `account_suspended` | The transaction exists and is already resolved (refund made, access revoked) | | `other` | The transaction exists, but is not resolved yet | A **Visa** alert arrives with the refund already made and has no deadline. See [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/). ## Before going to production - [ ] Credentials and signing key in environment variables or a secrets vault - [ ] Webhook validates the signature and the 5-minute window, and rejects with `401` - [ ] Webhook responds `2xx` in under 30 seconds and processes in a queue - [ ] The same `alert_id` processed twice causes no duplicate effect - [ ] Mastercard alert answered within 24 hours, automatically or by someone on call - [ ] Transactions with `transaction_id` filled in, if you use Dispute - [ ] `429` handled by waiting for `Retry-After` (see [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/#rate-limit)) ## Next steps - [Webhook payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/): every field of each event. - [Common problems](https://doc.rapidchargeback.com/en/referencia/problemas-comuns/): what to do when something does not work. - [Developer tools](https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/): the documentation in your AI assistant and the Postman collection. --- # Prevention > How Prevention resolves the dispute before the chargeback, answering the issuing bank with the order data from the transactions you already send. Página: https://doc.rapidchargeback.com/en/produtos/prevencao/visao-geral/ **Prevention** is Rapid's solution for real-time dispute deflection. When an issuing bank opens an inquiry about a purchase, Rapid answers right away with the original order data, and the dispute never becomes a chargeback. You do not call any Prevention endpoint: it works from the transactions you already send. The integration work is **sending the transactions with the right fields**, described in [Integration](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/). ## How it works 1. You send your sales transactions to Rapid through the [Transactions API](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/) 2. When the issuing bank inquires about a disputed transaction, Rapid automatically finds the matching transaction among the ones you sent 3. Rapid answers with the order data: merchant, items purchased, shipping address and buyer data 4. With that, the bank can close the dispute before it becomes a chargeback ## Types of protection Both happen on their own. You do not choose one or the other: Rapid uses whichever the transaction data allows, and both can apply to the same purchase. ### Purchase recognition Shows the cardholder, through their bank, the order details: what was bought, when and from whom. It resolves the dispute that starts because the person **did not recognize** the charge. It works with the minimum transaction fields. ### Fraud protection Proves that the buyer is the card owner, with signals from the purchase: IP, device, the customer's account in your system and shipping address. It can automatically close a **fraud** dispute. It requires two signals, described in [Integration](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/#for-fraud-protection). --- # Integration > What to send to enable Prevention: merchant data, minimum transaction fields and the ones that enable fraud protection. Página: https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/ To integrate Prevention, you send your sales transactions to Rapid through the **Transactions API**. See the [Transactions API documentation](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/) for the full endpoint details. ## Merchant data Before sending transactions, make sure your merchant is registered with complete information. The following merchant fields are **required** for Prevention to work: | Field | Description | |---|---| | `name` | Merchant name | | `merchant_url` | Merchant website URL | | `contact_phone` | Contact phone number | | `store_name` | Store name | ## Minimum transaction fields Besides the required fields of the Transactions API, Prevention needs the following to work: | Field | Why | |---|---| | `items[]` with `product_description` | Description of the products purchased. Without it there is nothing to show the issuing bank | | `order_number` | Order identification in the answer to the issuing bank (recommended) | ## Recommended fields The more data you send, the higher the chance of deflecting disputes. The table below shows the recommended fields and the impact of each: ### For purchase recognition | Field | Impact | |---|---| | `card_bin` | Improves the accuracy of the match with the disputed transaction | | `auth_code` | Improves transaction identification | | `customer.first_name` and `customer.last_name` | Helps the cardholder recognize the purchase | | `customer.email` | Helps the cardholder recognize the purchase | ### For fraud protection Fraud protection handles **fraud** disputes. It needs **two signals** that the buyer is the card owner: an identifier of the purchase (the anchor) and one more piece of data that confirms the person. To qualify a transaction, send one of the 3 combinations below. The anchor (`ip_address`, `device_id` or `device_fingerprint`) is **required**; next to it, send **at least one** field from the complements column. | Format | Required anchor | At least one of the complements | |---|---|---| | **Option 1** | `ip_address` | `customer.account_id`, `addresses` (shipping), `device_id` or `device_fingerprint` | | **Option 2** | `device_id` | `customer.account_id`, `addresses` (shipping) or `ip_address` | | **Option 3** | `device_fingerprint` | `customer.account_id`, `addresses` (shipping) or `ip_address` | #### Fields: format and restrictions | Field | Requirements | |---|---| | `ip_address` | The buyer's public IP at the time of purchase. Plain text, cannot be a hash. IPv4 or IPv6 | | `device_id` | Unique device identifier (e.g. IMEI). Plain text, at least 15 characters, cannot be a hash | | `device_fingerprint` | Fingerprint derived from device attributes (OS, model, version, etc.). At least 20 characters. Can be a hash | | `customer.account_id` | The buyer's login identifier in your system (email, username). A single value | | `addresses` (type: shipping) | Full shipping address: `street` (address1), `city`, `state` (region), `postal_code`, `country`. Cannot be a store address | > **You do not choose the option.** Send as many fields as you have: Rapid automatically uses the combination your transaction covers. For example, if you send `ip_address + device_id + account_id`, your transaction qualifies for both Option 1 and Option 2, and the combination with more fields maximizes the chance of deflection. #### Digital goods: do not send a shipping address If your company sells **digital goods** (software, SaaS, streaming, ebooks, online courses, services without physical delivery), **do not send `addresses` with type `shipping`**. Card network rules forbid sellers of digital goods from providing a shipping address, and sending one can **suspend fraud protection** for your account. For digital goods, focus on these complements: | Option | Anchor | Practical complements | |---|---|---| | 1 | `ip_address` | `customer.account_id`, `device_id`, `device_fingerprint` | | 2 | `device_id` | `customer.account_id`, `ip_address` | | 3 | `device_fingerprint` | `customer.account_id`, `ip_address` | `customer.account_id` and `ip_address` tend to be the most natural fields to capture in digital e-commerce. ## Full transaction example ```json { "merchant_id": "00000000-0000-0000-0000-000000000001", "transaction_date": "2026-04-01T15:30:00Z", "amount": 150.00, "currency": "USD", "card_last4": "4242", "card_bin": "424242", "order_number": "ORD-001", "auth_code": "AUTH999", "network": "visa", "descriptor": "LOJA EXEMPLO", "ip_address": "203.0.113.10", "device_id": "041C226BBD5A80020040105118304404", "external_source": "shopify", "external_id": "TXN-2026-001", "items": [ { "product_description": "Assinatura Premium - Mensal", "product_name": "Plano Premium", "quantity": 1, "unit_price": 150.00 } ], "customer": { "first_name": "João", "last_name": "Silva", "email": "joao@exemplo.com", "account_id": "joao@exemplo.com" }, "addresses": [ { "type": "shipping", "street": "Rua das Flores", "number": "123", "city": "São Paulo", "state": "SP", "postal_code": "01001000", "country": "BRA" } ] } ``` This example includes every recommended field for maximum deflection coverage. --- # Alert > How Alert warns you of a dispute before it becomes a chargeback, how the alert reaches your company and what to respond for each card network. Página: https://doc.rapidchargeback.com/en/produtos/alerta/visao-geral/ **Alert** is Rapid's solution for **early dispute notification**. (Former name: *Chargeback Alert*. The API path is still `/chargeback-alert/...`.) When a cardholder opens a dispute with the issuing bank, the network (Mastercard or Visa) signals the event to providers before it becomes a formal chargeback. Rapid receives these signals and delivers the alert to you in time to take action. In API payloads and responses, providers are identified by the slugs `ethoca_alerts` (Mastercard) and `verifi_rdr` (Visa). What you need to do depends on the card network: - **Mastercard** (via Ethoca): you have up to 24 hours to confirm whether you will refund, state that the transaction was already resolved, or that you did not find the transaction. Your answer goes back to Ethoca and can prevent the dispute from becoming a chargeback. - **Visa** (via Verifi RDR): the refund was already made automatically by the acquirer when the alert arrived. There is no response deadline; the status is used only for internal organization. ## How it works 1. The cardholder opens a dispute with the issuing bank 2. The card network signals the dispute to the provider (Ethoca for Mastercard, Verifi RDR for Visa) 3. The provider delivers the alert to Rapid 4. Rapid associates the alert with your company by `descriptor` (Ethoca) or BIN+CAID (Verifi) 5. Rapid delivers the alert to your application by webhook 6. Your application processes the alert and (for Ethoca) returns a status through the callback API To receive alerts, your company needs: - An **active webhook** configured in the dashboard (see [Webhook channel](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/)) - At least one **registered descriptor** (Ethoca) or **BIN+CAID** (Verifi) associated with your company ## Providers ### Ethoca (Mastercard) Alerts are associated with your company by the `descriptor`, the name that appears on the cardholder's statement. When the alert's descriptor matches one of yours, the alert is delivered. ### Verifi RDR (Visa) Alerts are associated by BIN + CAID. The deadline and what to respond for each card network are at the top of this page; the full detail is in [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/). ## Next steps - [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/): deadline details by card network. - [Webhook channel](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/): how to set up delivery. - [Status callback](https://doc.rapidchargeback.com/en/callback/visao-geral/): how to update the alert status. --- # Rules and deadlines > The statuses of an alert, the response deadline for each card network, converting other to account_suspended and when the alert expires. Página: https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/ ## Possible statuses Every alert starts with the `pending` status and changes according to the customer's response or as time passes. ### Statuses you can send through the callback | Status | When to use | |---|---| | `notfound` | The transaction was not found in your system | | `account_suspended` | The transaction was found and is already resolved (e.g. account suspended, refund made) | | `other` | The transaction was found but is not resolved yet | See [Update status](https://doc.rapidchargeback.com/en/callback/atualizar-status/) for the endpoint details. ### Statuses visible in the webhook payload Besides the three above that you send, the alert status in the webhook can appear as: | Status | Meaning | |---|---| | `pending` | Initial status of every alert received | | `expired` | Ethoca alert whose deadline passed with no response | --- ## Deadlines by provider ### Ethoca (Mastercard) **Response deadline: 24 hours** from receipt. - You must send `notfound`, `account_suspended` or `other` through the [callback](https://doc.rapidchargeback.com/en/callback/atualizar-status/) within this deadline - If the deadline passes with no response, the alert automatically becomes `expired`. Ethoca reads this as inaction and the dispute follows its normal course (it usually becomes a chargeback). **Converting `other → account_suspended`: up to 6 days** - If you first sent `other` because you were still investigating, you can later update it to `account_suspended` within 6 days of the original alert - This is the only transition allowed after an initial response **Final statuses cannot be changed:** - `notfound` is final - `account_suspended` is final - `expired` is final ### Verifi RDR (Visa) **No mandatory deadline.** - The refund was already made automatically by the acquirer before the alert even arrived - The status you send is only for internal organization in the dashboard - You can send any of the three statuses (`notfound`, `account_suspended`, `other`) at any time --- ## Expiration Rapid automatically marks Ethoca alerts as `expired` after 24h with no response. Verifi RDR alerts **do not expire**: they stay available in the dashboard indefinitely. --- ## Redelivery If the webhook delivery fails, Rapid makes up to **4 attempts** (1 initial + 3 retries) at intervals of 5, 15 and 30 minutes (see [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/)). If every attempt fails, the alert stays available in the dashboard and you can redeliver it from there (see [Manual redelivery](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/#manual-redelivery)). Redelivered alerts keep the same `alert_id`, so your application needs to be idempotent. --- # Dispute > How Dispute builds and submits the chargeback defense, what Rapid needs from you and the access revocation in fraud disputes. Página: https://doc.rapidchargeback.com/en/produtos/disputa/visao-geral/ **Dispute** is Rapid's solution for **automated chargeback defense**. While [Prevention](https://doc.rapidchargeback.com/en/produtos/prevencao/visao-geral/) answers the bank so the dispute is never filed and [Alert](https://doc.rapidchargeback.com/en/produtos/alerta/visao-geral/) warns you before it becomes a chargeback, Dispute acts afterwards: once the chargeback has been filed, Rapid builds and submits the defense (*second presentment*) within the card network's deadline. The defense is built from what you already send: the transaction, the recorded evidence and the signals captured at checkout. The more material, the better the chance of winning. ## How it works 1. **You** send the transaction, with the charge id in the gateway, and the evidence of the sale 2. **Gateway** records the chargeback when the cardholder opens the dispute 3. **Gateway → Rapid** notifies Rapid right away, because your gateway account is connected to Rapid (see below) 4. **Rapid** matches the dispute to your transaction by the charge id 5. **Rapid → You** notifies you by webhook, if the reason is fraud, so you revoke the buyer's access 6. **Rapid** assesses the case, builds the dossier with the evidence and submits the defense 7. **Gateway → Rapid** returns the outcome, which shows up in the dashboard Your work is in steps 1 and 5, highlighted: the material you send before anything happens and the access revocation. The rest is up to Rapid. ## What Rapid needs from you | What | Why | Where | |---|---|---| | Your payment gateway account connected to Rapid | it is how the dispute arrives; without the connection, no dispute shows up | Dashboard, under **Settings › Integrations**: just authorize, no code | | The transaction, with the charge id in the gateway | without it the dispute arrives with no history and no evidence | [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/) | | Evidence that supports the sale | it is the content of the defense | [Evidence](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/) and [Capture](https://doc.rapidchargeback.com/en/canais/capture/visao-geral/) | | An endpoint for the access revocation | it is required by card network rules | [Webhook](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/) | The point that breaks integrations most often is the transaction. The dispute finds the transaction by the charge identifier in the gateway, so the `transaction_id` field (or `external_source` plus `external_id`) has to carry that value. A transaction that is not found means a defense without the authentication data, without attached evidence and without the order history. ## Access revocation in fraud disputes When the dispute is about fraud, the card networks require the merchant to try to revoke the product or service delivered and to have a process to prevent recurrence. For that, Rapid fires the `dispute.fraud.revoke_access` event to your webhook, with the required actions and the deadline. It is the only point of Dispute where Rapid calls your system. Handle it automatically if possible: the faster the revocation, the smaller the loss. The format is in [Webhook payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/#disputefraudrevoke_access-event). If the delivery fails, the pending item shows up in the dashboard for someone on your team to confirm manually. ## Dispute states | State | What it means | |---|---| | `received` | arrived and is being assessed | | `submitted` | defense sent to the gateway, waiting for the outcome | | `won` | dispute won | | `lost` | dispute lost | | `accepted` | the case was not contested | | `needs_review` | Rapid could not identify the seller, and the team is reviewing it | Not every chargeback is contested. Rapid assesses each case: when card network rules give no right to contest, or when the available material does not support the defense, the case is marked as not contested instead of spending the deadline on a losing submission. ## Setup requirements - the **Dispute** product active for your company - your payment gateway account connected in the dashboard, under **Settings › Integrations** - an active webhook, if you want to receive the access revocation The gateway connection is done in the dashboard, with no code: you create a restricted key on the payment platform, paste it in the dashboard and register the address the dashboard shows. Rapid support helps with this step. ## Next steps - [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/): what to send so the dispute finds the transaction - [Evidence overview](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/): how to record evidence - [Capture overview](https://doc.rapidchargeback.com/en/canais/capture/visao-geral/): how to capture signals at checkout - [Webhook payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/): the format of the access revocation --- # List merchants > Lists your company's merchants, paginated, with the merchant_id that creating a transaction and sending a capture event require. Página: https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/ Lists your company's merchants (stores, brands or sellers). This is where the `merchant_id` comes from, which [create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/) and [send event from the server](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) require. Read only: creating and editing merchants is done in the dashboard. ## Endpoint ``` GET https://api.rapidchargeback.com/api/v1/merchants ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) ``` --- ## Query params All optional. | Param | Type | Default | Description | |---|---|---|---| | `page` | int (≥ 1) | `1` | Page | | `per_page` | int (1–100) | `20` | Items per page | | `merchant_ref` | string (up to 100) | - | The seller ID in **your** system, the same `merchant_ref` accepted in [create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/). Returns 0 or 1 item | | `status` | enum | - | `active` or `inactive` | The list is always paginated, in registration order (oldest first). To go through all of them, advance `page` until `page` reaches `total_pages`: a merchant registered along the way is added at the end, so you never skip or repeat an item. --- ## Examples ### All merchants ```bash curl -G "https://api.rapidchargeback.com/api/v1/merchants" \ --data-urlencode "per_page=100" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` ### By the seller ID in your system ```bash curl -G "https://api.rapidchargeback.com/api/v1/merchants" \ --data-urlencode "merchant_ref=seller-77" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` --- ## Response ### Success ```json { "data": [ { "id": "00000000-0000-0000-0000-000000000001", "name": "Loja Exemplo", "merchant_ref": null, "status": "active", "created_at": "2026-03-10T12:00:00.000Z" } ], "meta": { "page": 1, "per_page": 20, "total": 1, "total_pages": 1 } } ``` | Field | Description | |---|---| | `id` | The `merchant_id` the other APIs require | | `name` | Merchant name in the dashboard | | `merchant_ref` | The seller ID in your system, when the merchant was created through `merchant_ref`. `null` for those registered in the dashboard | | `status` | `active` or `inactive`: the registration status in the dashboard | | `created_at` | When it was registered | These are the only fields: the merchant's contact details, address and terms stay in the dashboard. ### No results ```json { "data": [], "meta": { "page": 1, "per_page": 20, "total": 0, "total_pages": 0 } } ``` ### Invalid parameter (e.g. `per_page=500`) ```json { "error": { "code": "VALIDATION_ERROR", "message": "Number must be less than or equal to 100" } } ``` --- ## Notes - **Only your company's merchants.** The company comes from the credential; there is no parameter to choose another one. - **A blocked company** receives `403 COMPANY_BLOCKED`, as in the other APIs. - **Auth, rate limit, error codes:** see [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/) and [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/). --- # Overview > Send, retrieve, update and remove the sales transactions that feed Prevention and Dispute, with Basic Auth and JSON. Página: https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/ The Transactions API lets you send, retrieve, update and remove your company's sales transactions on the Rapid platform. These transactions are used by the solutions you subscribe to in order to protect your business against disputes and chargebacks. ## How it works 1. Your application sends the sales transactions over HTTPS to the Rapid API 2. Rapid stores the transaction data (items, customer, addresses, payments, refunds) 3. The active solutions use this data automatically when needed ## Available endpoints | Method | Endpoint | Description | |---|---|---| | POST | `/api/v1/transactions` | Create a transaction | | POST | `/api/v1/transactions/batch` | Create multiple transactions (up to 1000) | | GET | `/api/v1/transactions/:id` | Retrieve a transaction | | GET | `/api/v1/transactions?external_id=` | Find a transaction by your system's ID | | PATCH | `/api/v1/transactions/:id` | Update a transaction | | DELETE | `/api/v1/transactions/:id` | Delete a transaction | ## Base URL ``` https://api.rapidchargeback.com/api/v1 ``` ## Authentication Every request uses **Basic Auth** with the `client_id` and `client_secret` generated in the dashboard. How to generate and send them is in [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ``` Authorization: Basic base64(client_id:client_secret) ``` ## Limits | Limit | Value | |---|---| | Requests per minute | 100 per IP | | Transactions per request (batch) | 1000 | ## Immutability protection Transactions that were already used by a solution (e.g. queried by **Prevention**) cannot be changed or deleted. In that case, the API returns `409 Conflict`. Transactions that have not been used yet can be freely updated or deleted. --- # Create transaction > Creates a single sales transaction. To create several transactions at once, see the Batch upload page. Página: https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/ Creates a single sales transaction. To create several transactions at once, see the [Batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/) page. ## Endpoint ``` POST https://api.rapidchargeback.com/api/v1/transactions ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` --- ## Required fields | Field | Type | Description | |---|---|---| | `merchant_id` **or** `merchant_ref` | string | Send **exactly one**: `merchant_id` (the merchant's UUID at Rapid, must belong to your company) **or** `merchant_ref` (the seller's ID in **your** system, up to 100 characters, for channels/platforms with several sellers). If `merchant_ref` is unknown, the merchant is **created** automatically with `merchant_name` (optional, up to 255 characters; ignored when the merchant already exists). Sending both → `422 VALIDATION_ERROR`. Your merchants and their `merchant_id` are in [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/). | | `external_id` | string | Transaction ID in the source system | | `amount` | number | Transaction amount (must be positive) | | `currency` | string | ISO 4217 code with 3 uppercase letters (e.g. `USD`, `BRL`) | | `transaction_date` | string | Transaction date in ISO 8601 format (e.g. `2026-04-01T15:30:00Z`) | | `card_last4` | string | Last 4 digits of the card (exactly 4 numeric digits) | | `items` | array | At least 1 item (see the item fields below) | ## Item fields ### Required | Field | Type | Description | |---|---|---| | `product_description` | string | Product description (up to 1000 characters; above that the request fails) | | `unit_price` | number | Unit price | ### Optional | Field | Type | Description | |---|---|---| | `product_name` | string | Product name | | `quantity` | number | Quantity | | `unit_of_measure` | string | Unit of measure | | `category` | string | Product category | --- ## Optional fields | Field | Type | Description | |---|---|---| | `external_source` | string | Source system (default: `custom`) | | `order_number` | string | Order number | | `transaction_id` | string | Transaction ID in the payment gateway. See the note below | | `arn` | string | Acquirer Reference Number | | `banknet_ref` | string | Banknet reference (Mastercard) | | `auth_code` | string | Authorization code | | `network` | string | Card network: `visa`, `mastercard`, `amex`, `discover`, `other` | | `card_bin` | string | Card BIN (6 to 8 numeric digits) | | `ip_address` | string | Buyer's IP address | | `clearing_datetime` | string | Clearing date (ISO 8601) | | `mcc` | string | Merchant Category Code | | `eci` | string | Electronic Commerce Indicator | | `pos_entry_mode` | string | POS entry mode | | `descriptor` | string | Billing descriptor | | `correlation_id` | string | Correlation ID | | `device_id` | string | Device ID (min. 15 characters) | | `device_fingerprint` | string | Device fingerprint (min. 20 characters) | | `customer` | object | Buyer data (see below) | | `addresses` | array | Shipping and/or billing addresses (see below) | | `payments` | array | Payment data (see below) | | `refunds` | array | Refund data (see below) | ### Size limits Every text field has a ceiling. Above it the response is `422 VALIDATION_ERROR` naming the field, never a server error. | Limit | Fields | |---|---| | 1000 | `product_description` | | 255 | `descriptor`, `merchant_name`, `product_name`, `device_fingerprint`, `street`, `reason` (refund) | | 200 | `billing_name` | | 100 | `external_id`, `order_number`, `transaction_id`, `arn`, `banknet_ref`, `correlation_id`, `merchant_ref`, `device_id`, `first_name`, `last_name`, `city`, `state`, `category` | | 64 | `method_masked`, `reference_number` | | 45 | `ip_address` (fits IPv6) | | 50 | `external_source`, `auth_code`, `account_id` | | 32 | `unit_of_measure` | | 30 | `phone`, `postal_code`, `wallet_indicator` | | 20 | `number` (address) | | 16 | `payment_type` | | 10 | `mcc`, `pos_entry_mode` | | 7 | `card_expiry` | | 5 | `eci` | | 2 | `cvv2_presence_indicator`, `avs_result` | `email` follows the 255 limit and must have a valid format. `currency`, `card_bin`, `card_last4`, `country` and `network` have a fixed format, described in the fields table. ### Note on `transaction_id` It is optional, but fill it in whenever the charge went through a payment gateway: this is the field Rapid uses to link a future chargeback to the transaction. Send the charge identifier in the gateway, not your order number (that one is `order_number`). If you use a gateway whose disputes reach Rapid and the field is empty, the chargeback is still received, but with no associated transaction: no history, no attached evidence and no authentication data, which is exactly the defense material. An equivalent alternative: send `external_source` with the gateway name and `external_id` with the charge id. --- ## `customer` object (all optional) | Field | Type | Description | |---|---|---| | `first_name` | string | First name | | `last_name` | string | Last name | | `email` | string | Email (valid format) | | `billing_name` | string | Name on the card | | `account_id` | string | Buyer's account ID in your system | | `phone` | string | Phone | ## `address` object (inside the `addresses` array) | Field | Type | Required | Description | |---|---|---|---| | `type` | string | Yes | `shipping` or `billing` | | `street` | string | No | Street | | `number` | string | No | Number | | `city` | string | No | City | | `state` | string | No | State/Region | | `postal_code` | string | No | Postal code | | `country` | string | No | ISO 3166-1 country code, 2 or 3 uppercase letters (`BR`/`BRA`, `US`/`USA`). Alpha-3 recommended. | ## Payment authentication fields (root, all optional) Inputs for [fraud protection](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/#for-fraud-protection) and 3-DS. The more you fill in, the stronger the deflection and the defense. | Field | Type | Description | |---|---|---| | `cvv2_presence_indicator` | string | CVV2 presence indicator in the authorization | | `cvv2_result` | string | CVV2 check result: `M`, `N`, `P`, `S`, `U` or `Y`, as it came in the authorization response | | `avs_result` | string | AVS result (address verification) | | `cardholder_verification_approved` | boolean | 3-DS/cardholder verification approved | | `authentication_response_type` | string | 3-DS authentication response type: `attempt` (attempt) or `confirm` (authentication confirmed) | | `wallet_indicator` | string | Digital wallet used (e.g. Apple Pay, Google Pay) | ### Buyer account (inside `customer`, all optional) They say whether the purchase was made by a known account, and since when it exists. They are the strongest defense material in a fraud dispute: a buyer with an old account who was logged in is hard to dispute as "I do not recognize it". | Field | Type | Description | |---|---|---| | `registered_at` | string (ISO 8601) | When the buyer's account was created in your system | | `registered_at_source` | string | Where `registered_at` comes from: `merchant_api` (account record in your system) or `client_declared` (your statement, without that record) | | `account_authenticated` | boolean | The buyer was logged into the account when purchasing | | `account_authenticated_source` | string | How you know it: `per_account` (verified in this purchase) or `onboarding_flag` (your store's rule, for example, every purchase requires login) | | `account_authenticated_at` | string (ISO 8601) | When the login happened | | `auth_method` | string | How the buyer logged in: `password`, `otp`, `2fa`, `biometric`, `oauth` or `other` | | `account_source_method` | string | Where the account data comes from: `merchant_api` or `client_declared` | | `checkout_type` | string | How the purchase was made: `guest` (no account), `authenticated` (existing account, logged in) or `registered_at_checkout` (account created during the purchase) | A value outside the lists responds `422 VALIDATION_ERROR`. ## `payment` object (all optional) | Field | Type | Description | |---|---|---| | `payment_type` | string | Payment type (credit, debit, pix, etc.) | | `method_masked` | string | Masked number | | `matched_payment` | boolean | Whether this payment was the one used (default: true) | | `card_bin` | string | BIN (6-8 digits) | | `card_last4` | string | Last 4 digits (4 numeric digits) | | `installments` | integer | Number of installments | | `card_expiry` | string | Expiry (MM/YYYY) | ## `refund` object | Field | Type | Required | Description | |---|---|---|---| | `amount` | number | Yes | Refund amount (positive) | | `currency` | string | Yes | Currency (3 uppercase letters, ISO 4217) | | `reference_number` | string | No | Refund identifier | | `reason` | string | No | Refund reason | | `refund_datetime` | string | No | Refund date (ISO 8601) | --- ## Validations - `transaction_date` must be in ISO 8601 with timezone - `currency` must be an ISO 4217 code (3 uppercase letters) - `amount` must be greater than zero - `card_last4` must have exactly 4 numeric digits - `card_bin` must have 6 to 8 numeric digits - `device_id` must have at least 15 characters - `device_fingerprint` must have at least 20 characters - The `external_source + external_id` combination cannot duplicate an existing transaction of your company ## Warnings The API returns notices (without blocking creation) when important optional fields are missing: | When | Notice | |---|---| | No `card_bin` | `card_bin missing: reduces match accuracy` | | No `order_number` | `order_number missing: recommended to identify the order in dispute responses` | | No `ip_address`, `device_id` **and** `device_fingerprint` | `ip_address, device_id and device_fingerprint missing: recommended, fraud protection relies on them` | **One** of the three identifiers is enough to clear the last notice: the IP is not required. And there is no address notice on purpose: whoever sells digital goods should not send a shipping address (see [fraud protection](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/#digital-goods-do-not-send-a-shipping-address)). The `warnings` key **only shows up** in the response when there is at least one notice. Do not count on `warnings: []`. --- ## Request example The credentials come from environment variables (`RAPID_CLIENT_ID` and `RAPID_CLIENT_SECRET`), never written in the code. **cURL** ```bash curl -X POST https://api.rapidchargeback.com/api/v1/transactions \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "merchant_id": "00000000-0000-0000-0000-000000000001", "external_source": "shopify", "external_id": "TXN-2026-001", "transaction_date": "2026-04-01T15:30:00Z", "amount": 150.00, "currency": "USD", "card_last4": "4242", "card_bin": "424242", "order_number": "ORD-001", "auth_code": "AUTH999", "network": "visa", "descriptor": "LOJA EXEMPLO", "ip_address": "203.0.113.10", "items": [ { "product_description": "Assinatura Premium - Mensal", "product_name": "Plano Premium", "quantity": 1, "unit_price": 150.00 } ], "customer": { "first_name": "João", "last_name": "Silva", "email": "joao@exemplo.com" }, "addresses": [ { "type": "shipping", "street": "Rua das Flores", "number": "123", "city": "São Paulo", "state": "SP", "postal_code": "01001000", "country": "BRA" } ] }' ``` **Node.js** ```javascript const credentials = Buffer.from(`${process.env.RAPID_CLIENT_ID}:${process.env.RAPID_CLIENT_SECRET}`).toString('base64') const response = await fetch('https://api.rapidchargeback.com/api/v1/transactions', { method: 'POST', headers: { Authorization: `Basic ${credentials}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ merchant_id: '00000000-0000-0000-0000-000000000001', external_source: 'shopify', external_id: 'TXN-2026-001', transaction_date: '2026-04-01T15:30:00Z', amount: 150.0, currency: 'USD', card_last4: '4242', card_bin: '424242', order_number: 'ORD-001', auth_code: 'AUTH999', network: 'visa', descriptor: 'LOJA EXEMPLO', ip_address: '203.0.113.10', items: [ { product_description: 'Assinatura Premium - Mensal', product_name: 'Plano Premium', quantity: 1, unit_price: 150.0, }, ], customer: { first_name: 'João', last_name: 'Silva', email: 'joao@exemplo.com', }, addresses: [ { type: 'shipping', street: 'Rua das Flores', number: '123', city: 'São Paulo', state: 'SP', postal_code: '01001000', country: 'BRA', }, ], }), }) const body = await response.json() if (!response.ok) throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`) console.log('Transaction created:', body.data.id) for (const warning of body.warnings ?? []) console.warn('Warning:', warning) ``` **Python** ```python import os import requests response = requests.post( "https://api.rapidchargeback.com/api/v1/transactions", auth=(os.environ["RAPID_CLIENT_ID"], os.environ["RAPID_CLIENT_SECRET"]), json={ "merchant_id": "00000000-0000-0000-0000-000000000001", "external_source": "shopify", "external_id": "TXN-2026-001", "transaction_date": "2026-04-01T15:30:00Z", "amount": 150.00, "currency": "USD", "card_last4": "4242", "card_bin": "424242", "order_number": "ORD-001", "auth_code": "AUTH999", "network": "visa", "descriptor": "LOJA EXEMPLO", "ip_address": "203.0.113.10", "items": [ { "product_description": "Assinatura Premium - Mensal", "product_name": "Plano Premium", "quantity": 1, "unit_price": 150.00, } ], "customer": { "first_name": "João", "last_name": "Silva", "email": "joao@exemplo.com", }, "addresses": [ { "type": "shipping", "street": "Rua das Flores", "number": "123", "city": "São Paulo", "state": "SP", "postal_code": "01001000", "country": "BRA", } ], }, timeout=30, ) body = response.json() if not response.ok: raise RuntimeError(f"{response.status_code} {body['error']['code']}: {body['error']['message']}") print("Transaction created:", body["data"]["id"]) for warning in body.get("warnings", []): print("Warning:", warning) ``` **PHP** ```php '00000000-0000-0000-0000-000000000001', 'external_source' => 'shopify', 'external_id' => 'TXN-2026-001', 'transaction_date' => '2026-04-01T15:30:00Z', 'amount' => 150.00, 'currency' => 'USD', 'card_last4' => '4242', 'card_bin' => '424242', 'order_number' => 'ORD-001', 'auth_code' => 'AUTH999', 'network' => 'visa', 'descriptor' => 'LOJA EXEMPLO', 'ip_address' => '203.0.113.10', 'items' => [ [ 'product_description' => 'Assinatura Premium - Mensal', 'product_name' => 'Plano Premium', 'quantity' => 1, 'unit_price' => 150.00, ], ], 'customer' => [ 'first_name' => 'João', 'last_name' => 'Silva', 'email' => 'joao@exemplo.com', ], 'addresses' => [ [ 'type' => 'shipping', 'street' => 'Rua das Flores', 'number' => '123', 'city' => 'São Paulo', 'state' => 'SP', 'postal_code' => '01001000', 'country' => 'BRA', ], ], ]; $ch = curl_init('https://api.rapidchargeback.com/api/v1/transactions'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_USERPWD => getenv('RAPID_CLIENT_ID') . ':' . getenv('RAPID_CLIENT_SECRET'), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode($transaction), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $raw = curl_exec($ch); if ($raw === false) { throw new RuntimeException('Network error: ' . curl_error($ch)); } $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $body = json_decode($raw, true); if ($status >= 400) { throw new RuntimeException("$status {$body['error']['code']}: {$body['error']['message']}"); } echo 'Transaction created: ', $body['data']['id'], PHP_EOL; foreach ($body['warnings'] ?? [] as $warning) { echo 'Warning: ', $warning, PHP_EOL; } ``` --- ## Response examples ### Success ```json { "data": { "id": "00000000-0000-0000-0000-000000000042", "company_id": "...", "merchant_id": "00000000-0000-0000-0000-000000000001", "external_id": "TXN-2026-001", "amount": 150.00, "currency": "USD", "transaction_date": "2026-04-01T15:30:00.000Z", "card_last4": "4242", "created_at": "2026-04-01T15:31:00.000Z", "transaction_items": [...], "transaction_customers": [...], "transaction_addresses": [...] } } ``` ### Duplicate ```json { "error": { "code": "TRANSACTION_DUPLICATE", "message": "Transaction already exists" } } ``` ### Validation error ```json { "error": { "code": "VALIDATION_ERROR", "message": "card_last4 must be exactly 4 digits" } } ``` --- # Batch upload > Creates multiple transactions in a single request (up to 1000 per call). Página: https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/ Creates multiple transactions in a single request (up to 1000 per call). Use batch upload when you need to sync large volumes (e.g. initial load, reprocessing a whole day). Processing is per transaction: the success of one does not depend on the success of the others. ## Endpoint ``` POST https://api.rapidchargeback.com/api/v1/transactions/batch ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` --- ## Request body | Field | Type | Description | |---|---|---| | `transactions` | array | List of transactions (min. 1, max. 1000) | Each array item follows the same schema as [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/): the same required, optional and nested fields and validations. --- ## Validations - The `transactions` array must have between 1 and 1000 items. A problem **in the envelope** (array missing, empty or above 1000) rejects the whole request - The request body can be up to **5 MB**, which covers 1000 transactions with every field filled in. Above that the response is `413 PAYLOAD_TOO_LARGE` and nothing is created: split it into smaller batches - Inside the array, **each transaction is validated individually**: invalid field, missing required field, text above the limit, duplicate, nonexistent merchant. All of these become an error for that row - An error in one row does **not** block the others: the valid ones are created and the response says which ones failed - A duplicate inside the batch itself (same `external_source` and `external_id` twice) creates the first and fails the second --- ## Request example ```bash curl -X POST https://api.rapidchargeback.com/api/v1/transactions/batch \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "transactions": [ { "merchant_id": "00000000-0000-0000-0000-000000000001", "external_source": "custom", "external_id": "TXN-2026-001", "transaction_date": "2026-04-01T10:00:00Z", "amount": 150.00, "currency": "USD", "card_last4": "4242", "items": [{ "product_description": "Item A", "unit_price": 150.00 }] }, { "merchant_id": "00000000-0000-0000-0000-000000000001", "external_source": "custom", "external_id": "TXN-2026-002", "transaction_date": "2026-04-01T11:00:00Z", "amount": 200.00, "currency": "BRL", "card_last4": "1234", "items": [{ "product_description": "Item B", "unit_price": 200.00 }] } ] }' ``` --- ## Response examples The batch responds **`200`**, not `201`: in a call that creates some rows and fails others, the status alone does not tell the result. The body does. Always check `failed` and `errors`, never just the HTTP code. ### Mixed results (200) ```json { "data": { "created": 1, "failed": 1, "results": [ { "external_id": "TXN-2026-001", "tx_id": "00000000-0000-0000-0000-000000000042", "warnings": ["card_bin missing: reduces match accuracy"] } ], "errors": [ { "index": 1, "external_id": "TXN-2026-002", "status": 409, "error": "Transaction already exists" } ] } } ``` Each item in `results` contains: - `external_id`: identifies the transaction in your system. - `tx_id`: UUID generated by Rapid (keep it for future operations). - `warnings`: notices about missing fields (they do not block creation). Each item in `errors` contains: - `index`: position of the row in the array you sent, starting at zero. This is how you find the row when the invalid field is the `external_id` itself. - `external_id`: identifies which transaction failed, or an empty string if the value sent was not valid. - `status`: the equivalent HTTP code of the error (`422` validation, `404` nonexistent merchant, `409` duplicate). - `error`: descriptive message. `created + failed` is always the total number of rows sent. `results` and `errors` come out in input order. ### All created successfully (200) ```json { "data": { "created": 2, "failed": 0, "results": [ { "external_id": "TXN-2026-001", "tx_id": "uuid-1", "warnings": [] }, { "external_id": "TXN-2026-002", "tx_id": "uuid-2", "warnings": [] } ], "errors": [] } } ``` ### Row with an invalid field (200) A row that fails validation does not bring down the others: it shows up in `errors` with `status: 422`. ```json { "data": { "created": 1, "failed": 1, "results": [ { "external_id": "TXN-2026-001", "tx_id": "uuid-1", "warnings": [] } ], "errors": [ { "index": 1, "external_id": "TXN-2026-002", "status": 422, "error": "Currency must be 3 uppercase letters (ISO 4217)" } ] } } ``` ### Batch validation error (outside the array) If the `transactions` array is empty, missing or exceeds 1000 items, the whole request is rejected: ```json { "error": { "code": "VALIDATION_ERROR", "message": "Array must contain at most 1000 element(s)" } } ``` --- ## Batch behaviors worth knowing - An **unknown** `merchant_ref` **creates the seller** (with `merchant_name`, if sent) and does not return 404. - A duplicate **inside the same batch** (same `external_source`/`external_id` twice) fails on the second row with `TRANSACTION_DUPLICATE`; the first one is created. - The order of `results` is the input order. --- # Retrieve transaction > Returns the full data of a transaction, by Rapid's ID or by your system's ID, including items, customer, addresses, payments and refunds. Página: https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/ Returns the full data of a transaction, including items, customer, addresses, payments and refunds. You can search by the ID Rapid returned at creation or, if you did not keep it, [by your system's ID](#by-your-systems-id). ## Endpoint ``` GET https://api.rapidchargeback.com/api/v1/transactions/:id ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) ``` --- ## URL parameter | Parameter | Type | Description | |---|---|---| | `id` | string (UUID) | Transaction ID | --- ## Request example ```bash curl -X GET https://api.rapidchargeback.com/api/v1/transactions/00000000-0000-0000-0000-000000000042 \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` --- ## Response examples ### Success ```json { "data": { "id": "00000000-0000-0000-0000-000000000042", "company_id": "00000000-0000-0000-0000-0000000000c1", "merchant_id": "00000000-0000-0000-0000-000000000001", "external_id": "TXN-2026-001", "external_source": "shopify", "order_number": "ORD-001", "auth_code": "AUTH999", "arn": null, "network": "visa", "amount": "150.00", "currency": "USD", "transaction_date": "2026-04-01T15:30:00.000Z", "card_bin": "424242", "card_last4": "4242", "descriptor": "LOJA EXEMPLO", "ip_address": "203.0.113.10", "created_at": "2026-04-01T15:31:00.000Z", "updated_at": "2026-04-01T15:31:00.000Z", "transaction_items": [ { "id": "...", "product_description": "Assinatura Premium - Mensal", "product_name": "Plano Premium", "quantity": 1, "unit_price": "150.00" } ], "transaction_customers": [ { "id": "...", "first_name": "João", "last_name": "Silva", "email": "joao@exemplo.com" } ], "transaction_addresses": [ { "id": "...", "type": "shipping", "street": "Rua das Flores", "number": "123", "city": "São Paulo", "state": "SP", "postal_code": "01001000", "country": "BRA" } ], "transaction_payments": [], "transaction_refunds": [] } } ``` ### Transaction not found ```json { "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction not found" } } ``` --- ## By your system's ID If you did not keep the `id` Rapid returned at creation, find the transaction by the identifier **you** sent: `external_source` plus `external_id`. It is the same combination that prevents duplicates at creation, so the response has **at most one** transaction. ``` GET https://api.rapidchargeback.com/api/v1/transactions?external_source=shopify&external_id=TXN-2026-001 ``` | Param | Type | Required | Description | |---|---|---|---| | `external_id` | string (up to 100) | Yes | The `external_id` sent at creation | | `external_source` | string (up to 50) | No | The `external_source` sent at creation. Without it, `custom` applies, the same default as at creation | This is **not** a transaction listing: without `external_id` the response is `422`. With the `id` in hand, [update](https://doc.rapidchargeback.com/en/canais/transactions/atualizar-transacao/) and [delete](https://doc.rapidchargeback.com/en/canais/transactions/deletar-transacao/) work as usual. ### Example ```bash curl -G "https://api.rapidchargeback.com/api/v1/transactions" \ --data-urlencode "external_source=shopify" \ --data-urlencode "external_id=TXN-2026-001" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` ### Found `200` with a one-item list, in the same format as the lookup by ID: ```json { "data": [ { "id": "00000000-0000-0000-0000-000000000042", "external_source": "shopify", "external_id": "TXN-2026-001", "...": "other fields, as in the lookup by ID" } ] } ``` ### Not found `200` with an empty list. A transaction from another company also comes back empty: the search is always within yours. ```json { "data": [] } ``` ### Without `external_id` ```json { "error": { "code": "VALIDATION_ERROR", "message": "external_id is required" } } ``` --- # Update transaction > Updates an existing transaction. Every field is optional: send only what you want to change. Página: https://doc.rapidchargeback.com/en/canais/transactions/atualizar-transacao/ Updates an existing transaction. Every field is optional: send only what you want to change. When child records are sent (`items`, `addresses`, `payments`, `refunds`, `customer`), they **fully replace** the existing records. ## Endpoint ``` PATCH https://api.rapidchargeback.com/api/v1/transactions/:id ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` --- ## URL parameter | Parameter | Type | Description | |---|---|---| | `id` | string (UUID) | Transaction ID (returned in the `id` field of the creation response) | Did not keep the `id`? Find the transaction [by your system's ID](https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/#by-your-systems-id). --- ## Accepted fields Every field accepted by [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/) can be sent here, all optional: - Fields sent replace the current value - Fields omitted keep the current value - Child records sent (`items`, `addresses`, etc.) fully replace the existing ones --- ## Validations - If `merchant_id` is changed, the new merchant must belong to your company - If `external_id` or `external_source` are changed, the combination cannot duplicate another existing transaction - If the transaction was already used by a solution (e.g. queried by **Prevention**), the update is blocked with `409 Conflict` --- ## Request example ### Update only the ARN and auth_code ```bash curl -X PATCH https://api.rapidchargeback.com/api/v1/transactions/00000000-0000-0000-0000-000000000042 \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "arn": "74537119547024600128228", "auth_code": "AUTH999" }' ``` ### Update items (replaces all existing items) ```bash curl -X PATCH https://api.rapidchargeback.com/api/v1/transactions/00000000-0000-0000-0000-000000000042 \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "amount": 300.00, "items": [ { "product_description": "Plano Premium", "unit_price": 150.00 }, { "product_description": "Taxa de Setup", "unit_price": 150.00 } ] }' ``` --- ## Response examples ### Success ```json { "data": { "id": "00000000-0000-0000-0000-000000000042", "merchant_id": "00000000-0000-0000-0000-000000000001", "amount": 300.00, "transaction_items": [ { "product_description": "Plano Premium", "unit_price": 150.00 }, { "product_description": "Taxa de Setup", "unit_price": 150.00 } ] } } ``` ### Transaction not found ```json { "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction not found" } } ``` ### Immutable transaction (already used by a solution) ```json { "error": { "code": "TRANSACTION_IMMUTABLE", "message": "Transaction cannot be modified, it has been used by a network event" } } ``` ### Duplicate external_id ```json { "error": { "code": "TRANSACTION_DUPLICATE", "message": "Another transaction with this external_source/external_id already exists" } } ``` --- # Delete transaction > Deletes a transaction and all its related data (items, customer, addresses, payments, refunds). Página: https://doc.rapidchargeback.com/en/canais/transactions/deletar-transacao/ Deletes a transaction and all its related data (items, customer, addresses, payments, refunds). ## Endpoint ``` DELETE https://api.rapidchargeback.com/api/v1/transactions/:id ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) ``` --- ## URL parameter | Parameter | Type | Description | |---|---|---| | `id` | string (UUID) | Transaction ID | Did not keep the `id`? Find the transaction [by your system's ID](https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/#by-your-systems-id). --- ## Validations - If the transaction was already used by a solution (e.g. queried by **Prevention**), deletion is blocked with `409 Conflict` --- ## Request example ```bash curl -X DELETE https://api.rapidchargeback.com/api/v1/transactions/00000000-0000-0000-0000-000000000042 \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` --- ## Response examples ### Success ``` HTTP/1.1 204 No Content ``` No response body. ### Transaction not found ```json { "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction not found" } } ``` ### Immutable transaction ```json { "error": { "code": "TRANSACTION_IMMUTABLE", "message": "Transaction cannot be modified, it has been used by a network event" } } ``` --- # Error codes > The HTTP statuses and error codes of the Transactions API, with the exact messages and the error format in batch upload. Página: https://doc.rapidchargeback.com/en/canais/transactions/codigos-de-erro/ ## HTTP codes | Code | Meaning | |---|---| | 200 | Success (GET, PATCH, batch) | | 201 | Transaction created (single POST) | | 204 | Transaction deleted (DELETE) | | 401 | Missing or invalid credentials | | 403 | Company blocked | | 404 | Transaction or merchant not found | | 409 | Conflict (duplicate or immutable transaction) | | 422 | Validation error (invalid fields) | | 429 | Too many requests (limit: 100 per minute per IP) | | 500 | Internal server error | --- ## Error response format Every error response follows the same format: ```json { "error": { "code": "ERROR_CODE", "message": "Error description" } } ``` --- ## Possible error codes ### Authentication | Code | HTTP | Message | |---|---|---| | `UNAUTHORIZED` | 401 | `Missing or invalid Authorization header` | | `UNAUTHORIZED` | 401 | `Invalid Basic Auth format` | | `UNAUTHORIZED` | 401 | `Missing client_id or client_secret` | | `UNAUTHORIZED` | 401 | `Invalid credentials` | | `COMPANY_BLOCKED` | 403 | `Company is blocked` | ### Validation | Code | HTTP | Message | |---|---|---| | `VALIDATION_ERROR` | 422 | Validator message (e.g. `card_last4 must be exactly 4 digits`) | ### Transactions | Code | HTTP | Message | |---|---|---| | `TRANSACTION_NOT_FOUND` | 404 | `Transaction not found` | | `TRANSACTION_DUPLICATE` | 409 | `Transaction already exists` | | `TRANSACTION_DUPLICATE` | 409 | `Another transaction with this external_source/external_id already exists` | | `TRANSACTION_IMMUTABLE` | 409 | `Transaction cannot be modified: it has been used by a network event` | | `MERCHANT_NOT_FOUND` | 404 | `Merchant not found` | ### Rate limit | Code | HTTP | Message | |---|---|---| | `RATE_LIMIT_EXCEEDED` | 429 | `Rate limit exceeded, retry in X seconds` | --- ## Errors in batch upload On the batch endpoint, individual errors are returned in the `errors` array inside `data`, without stopping the processing of the other transactions: ```json { "data": { "created": 1, "failed": 2, "results": [ { "external_id": "TXN-2026-001", "tx_id": "uuid-1", "warnings": [] } ], "errors": [ { "index": 1, "external_id": "TXN-2026-002", "status": 409, "error": "Transaction already exists" }, { "index": 2, "external_id": "TXN-2026-003", "status": 404, "error": "Merchant not found" } ] } } ``` Each error includes: - `external_id`: identifies which transaction failed. - `status`: the equivalent HTTP code of the error. - `error`: descriptive message. --- # Best practices > How to send transactions reliably: what to send, data format, credential security, response handling and immutability. Página: https://doc.rapidchargeback.com/en/canais/transactions/boas-praticas/ ## Sending data - **Send every field you have.** The more data you provide, the more effective the subscribed solutions are. Fields such as `card_bin`, `order_number`, `ip_address` and `addresses` are especially important. - **Use batch upload for large volumes.** Instead of sending one transaction per request, group up to 1000 transactions on the `/transactions/batch` endpoint to reduce network overhead. - **Keep `external_source` and `external_id` consistent.** These fields are used for duplicate detection. Use unique and stable identifiers from your source system. - **Send transactions as soon as possible.** The closer to the moment of sale, the better the coverage of the prevention solutions. ## Data format - **`transaction_date` in ISO 8601.** Use the full format with timezone (e.g. `2026-04-01T15:30:00Z`). - **`currency` in uppercase.** Use the 3-letter uppercase ISO 4217 code (e.g. `USD`, `BRL`, `EUR`). - **`card_last4` as a string.** Send it as a string of exactly 4 digits (e.g. `"0042"`, not `42`). - **`merchant_id` as a UUID.** Use the merchant UUID as returned by the API or the dashboard. ## Security - **Never expose your `client_secret`.** Treat it like a password. Do not include it in frontend code, public repositories or logs. - **Always use HTTPS.** Every request must be made over HTTPS. - **Implement retry with progressive backoff.** On a 500 error (internal error), wait before trying again: 1s, 2s and then 4s between attempts. On a `429` there is no need to guess: the response carries the `Retry-After` header with the exact seconds until the window resets (see [Rate limit](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/#rate-limit)). ## Response handling - **Single transaction:** check the `data` field for the created transaction data and `warnings` for notices. - **Batch upload:** iterate over `results` (successes with `tx_id` and `warnings`) and `errors` (failures with `external_id`, `status` and message). - **Pay attention to `warnings`.** They do not block creation, but they point to missing fields that affect how effective the solutions are. - **Store the returned `id`.** You will need it to retrieve, update or delete the transaction. ## Immutability - **Fix mistakes before the first query.** Transactions used by the solutions (e.g. queried by **Prevention**) become immutable. Use PATCH to fix data while the transaction has not been used yet. --- # Overview > How Rapid delivers alerts and events to your system by HTTPS webhook: URL registration, signing key and delivery flow. Página: https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/ Rapid delivers alerts and events to the customer's system through an **HTTPS POST webhook**. Your application exposes a URL, and Rapid sends requests to it every time there is an alert to notify. The webhook is the universal delivery mechanism. Today two products use the channel: - **Alert**: `chargeback_alert.received`: a new alert arrived. - **Dispute**: `dispute.fraud.revoke_access`: a fraud dispute arrived and the card network requires cutting off the cardholder's access. There is a single URL; the `event` field (and the `X-Webhook-Event` header) says which event arrived. Any future product uses the same channel. Internally, providers are identified by slugs in payloads and responses: `ethoca_alerts` (Mastercard) and `verifi_rdr` (Visa). ## Flow in short 1. You register your endpoint URL in the Rapid dashboard; Rapid generates the signing key and shows it only once 2. When an alert is received and matched to your company, Rapid queues the delivery 3. Rapid makes an `HTTP POST` to your URL with the event payload 4. Your application validates the signature in the `X-Webhook-Signature` header, processes the event and returns `200` or `204` 5. If your application returns an error or does not respond, Rapid tries again (see [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/)) ## Configuration - Each company can have **one active webhook at a time**. - The signing key (`secret`) is generated by Rapid at registration, shown **only once** and used to validate the HMAC-SHA256 signature. Lost it? Generate another one in **Settings › API** (the previous one stops working immediately). - If the webhook is inactive, events are still stored and visible in the dashboard, but they are not delivered. An access revocation that was not delivered shows up as a pending item on the dispute, for manual handling. ## Next steps - [Payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/): full structure of the body sent. - [Authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/): how to validate `X-Webhook-Signature`. - [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/): retry policy. - [Best practices](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/): idempotency, timeout, fast response. --- # Payload > The events Rapid sends by webhook: headers, the fields of each body, full examples and the difference between the v2 and legacy formats. Página: https://doc.rapidchargeback.com/en/canais/webhook/payload/ Rapid sends an `HTTP POST` with `Content-Type: application/json` to the configured URL. The body is a JSON object and the `event` field (also in the `X-Webhook-Event` header) says what happened. ## Events | Event | Product | When it fires | Body | |---|---|---|---| | `chargeback_alert.received` | Alert | An alert was received and matched to your company | [Alert received](#chargeback_alertreceived-event) | | `dispute.fraud.revoke_access` | Dispute | A **fraud** dispute arrived (Visa 10.4 / Mastercard 4837) and the card network requires you to cut off the cardholder's access | [Access revocation](#disputefraudrevoke_access-event) | The same URL receives every event: use `event` (or the header) to route. If you only handle one of them, respond `200` to the others even without processing them. There are **two formats**, set by Rapid on your account: - **`v2`**: the default for new accounts. Described on this page (headers, signature, body). - **`legacy`**: accounts migrated from the previous system. Same body and headers as the old system, **without a signature**. Described in the [Legacy format](#legacy-format) section at the end of the page. The `legacy` format applies **only** to `chargeback_alert.received`: the other events always go out in `v2`, even on those accounts. To find out or change your account's format, contact Rapid support. ## Method and headers (`v2` format) ``` POST https://your-system.com/webhooks/rapid Content-Type: application/json X-Webhook-Signature: sha256= X-Webhook-Timestamp: 1790000000 X-Webhook-Event: chargeback_alert.received X-Webhook-Reference-Id: ``` - `X-Webhook-Signature`: HMAC-SHA256 of `${X-Webhook-Timestamp}.${body}` with your signing key. See [Authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). - `X-Webhook-Timestamp`: when Rapid signed, in seconds since the epoch. It is part of the signature (anti-replay) and must be checked against a 5-minute tolerance window. - `X-Webhook-Event`: the event type (see [Events](#events)). Use it for routing. - `X-Webhook-Reference-Id`: the event's reference ID: `alert_id` in `chargeback_alert.received`, `dispute_id` in `dispute.fraud.revoke_access`. Allows deduplication without parsing the body. > The `clientid`/`clientkey` headers are **not** sent in the `v2` format: the signature is what authenticates the delivery. They are still sent only in the `legacy` format (see [Legacy format](#legacy-format)). --- ## `chargeback_alert.received` event ### Body structure | Field | Type | Description | Required | |---|---|---|---| | `event` | string | `chargeback_alert.received` | Yes | | `alert_id` | string (UUID) | Unique ID of the alert at Rapid; use it to call the search and status update endpoints | Yes | | `provider_alert_id` | string | ID of the alert in the provider's system. Useful for cross-reference when dealing directly with the provider's support | Yes | | `provider` | string | Source of the alert: `ethoca_alerts` or `verifi_rdr` | Yes | | `amount` | number \| null | Amount of the disputed transaction (can be `null` when the provider does not send it) | Yes | | `currency` | string | ISO 4217 code with 3 uppercase letters | Yes | | `card_last4` | string | Last 4 digits of the card | Yes | | `card_bin` | string | Card BIN (6-8 digits) | Yes | | `transaction_date` | string | Date of the original transaction (ISO 8601, `"2026-04-01T00:00:00.000Z"`) | Yes | | `descriptor` | string | Name shown on the cardholder's statement | Yes | | `arn` | string | Acquirer Reference Number | No | | `caid` | string | Card Acceptor ID | No | | `auth_code` | string | Authorization code of the transaction | No | | `alert_type` | string | Alert type (e.g. `fraud`, `dispute`) | No | | `reason_code` | string | Dispute reason code | No | | `issuer` | string | Card issuing bank | No | | `installment_number` | number \| null | Current installment | No | | `installment_count` | number \| null | Total number of installments | No | | `status` | string | Initial status of the alert. Always `pending` on receipt | Yes | | `received_at` | string | When Rapid received the alert (ISO 8601) | Yes | | `expires_at` | string \| null | Response deadline (ISO 8601), only for Ethoca | No | | `merchant` | object \| null | Data of the matched merchant (see below); `null` if the alert has not been matched yet | Yes | #### `merchant` object | Field | Type | Description | |---|---|---| | `id` | string (UUID) | Merchant ID at Rapid | | `name` | string | Merchant name | --- ### Payload example ```json { "event": "chargeback_alert.received", "alert_id": "00000000-0000-0000-0000-000000000099", "provider_alert_id": "2UBOD4MKAI42RPXEYU4UVQPS", "provider": "ethoca_alerts", "amount": 150.00, "currency": "USD", "card_last4": "4242", "card_bin": "424242", "transaction_date": "2026-04-01T00:00:00.000Z", "descriptor": "LOJA EXEMPLO", "arn": "74537119547024600128228", "caid": "123456789", "auth_code": "123456", "alert_type": "fraud", "reason_code": "4853", "issuer": "Chase Bank", "installment_number": null, "installment_count": null, "status": "pending", "received_at": "2026-04-14T10:00:00Z", "expires_at": "2026-04-15T10:00:00Z", "merchant": { "id": "00000000-0000-0000-0000-000000000001", "name": "Minha Loja" } } ``` --- ## `dispute.fraud.revoke_access` event Fired when Rapid receives a **fraud** dispute. The card networks require the merchant to attempt to revoke the goods or services provided to the cardholder and to have a process to prevent reoccurrence (Visa Core Rules §10.4.4.3). The faster the cut-off, the smaller the loss: handle this event automatically if possible. The `merchant` field says **which of your stores** made the sale: delivery always goes to the company's single webhook, and you route internally. ### Body structure | Field | Type | Description | Required | |---|---|---|---| | `event` | string | `dispute.fraud.revoke_access` | Yes | | `dispute_id` | string (UUID) | ID of the dispute at Rapid. Use it as the idempotency key; the dispute shows up in the dashboard, under Disputes | Yes | | `merchant` | object \| null | Matched store (`id`, `name`); `null` if the dispute has not been matched yet | Yes | | `network` | string | Card network: `visa`, `mastercard`, … | Yes | | `condition` | string \| null | Card network condition or reason (e.g. `10.4`, `4837`) | No | | `transaction_id` | string \| null | ID of the transaction on the network, when sent by the acquirer | No | | `customer_ref` | string \| null | Customer identifier you sent in the transaction (`account_id`), when there is one | No | | `order_ref` | string | Order reference: order number, your external ID or, if neither exists, the `dispute_id` | Yes | | `reason` | string | Always `fraud_dispute_received` | Yes | | `is_account_takeover` | boolean | `true` when there are signs of account takeover (device and IP differ from the customer's history) | Yes | | `required_actions` | string[] | Required actions: `revoke_access`, `prevent_reoccurrence` and, on account takeover, `re_authenticate` | Yes | | `citation` | object | The rule behind the requirement (`section`, `visa_id`, `page`, `quote_en`, `ruleset_version`) | Yes | | `emitted_at` | string | When Rapid emitted the event (ISO 8601) | Yes | | `deadline_hint` | string | Dispute response deadline (ISO 8601) or `as_soon_as_possible` when no deadline is known | Yes | ### Payload example ```json { "event": "dispute.fraud.revoke_access", "dispute_id": "00000000-0000-0000-0000-0000000000d1", "merchant": { "id": "00000000-0000-0000-0000-000000000001", "name": "Minha Loja" }, "network": "visa", "condition": "10.4", "transaction_id": "ch_3Qx0000000", "customer_ref": "acct-8842", "order_ref": "PED-5078", "reason": "fraud_dispute_received", "is_account_takeover": false, "required_actions": ["revoke_access", "prevent_reoccurrence"], "citation": { "section": "10.4.4.3", "visa_id": "0030642", "page": 634, "quote_en": "An Acquirer must ensure that its Merchant attempts to revoke provision of goods or services from the Cardholder after a Dispute category 10 (Fraud) Dispute and that the Merchant has a process in place to prevent reoccurrence by the Cardholder.", "ruleset_version": "visa-core-rules-2026-04-18" }, "emitted_at": "2026-09-30T14:25:00.000Z", "deadline_hint": "2026-10-14T14:20:00.000Z" } ``` ### Confirming what you did (optional) Responding `200` already closes the delivery. If you want to record what was done, return a JSON body with the fields below. It shows up in the dashboard, on the dispute, as confirmation from your integration. An unknown key invalidates the whole body (the delivery is still valid, it just does not record the confirmation). | Field | Type | Description | |---|---|---| | `status` | string | `done`, `partial`, `refused`, `not_applicable` or `accepted` | | `actions_taken` | string[] | Which of the `required_actions` you carried out | | `revoked_at` | string | When access was cut off (ISO 8601) | | `note` | string | Free-text note, up to 500 characters | ```json { "status": "done", "actions_taken": ["revoke_access", "prevent_reoccurrence"], "revoked_at": "2026-09-30T14:25:03Z", "note": "account suspended and card blocked for new purchases" } ``` **Idempotency:** Rapid emits **one** event per dispute (`dispute_id` is unique). Retries repeat the same `dispute_id`, so handle it by the key instead of counting calls. If we cannot deliver it, the dispute shows up in the dashboard with the revocation pending, and the customer confirms it manually there. --- ## Expected response Your application must return one of the following codes to confirm receipt: | Code | Meaning | |---|---| | `2xx` | Success. `200`, `201`, `202` and `204` are all equivalent; the body is optional | Any other code (including `4xx` and `5xx`) is treated as a failure and triggers a retry. Register the final URL: a `301`, `302` or `303` on your URL counts as a failure (see [Redirects](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/#redirects)). See [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/). In `dispute.fraud.revoke_access`, the body of the `200` can carry the confirmation of what you did (see [above](#confirming-what-you-did-optional)). For the other events the body is ignored. --- ## Fields that may evolve New fields may be added to the payload in the future. Your application must **ignore unknown fields** instead of failing. Never change or remove fields while processing; only read them. --- ## Legacy format Accounts migrated from the previous system receive the **same POST as before**. Headers: `Content-Type: application/json`, `x-source: rapid`, `clientid`, `clientkey`, and **no** `X-Webhook-Signature`, `X-Webhook-Event` or `X-Webhook-Reference-Id`. | Field | Type | Description | |---|---|---| | `alert_id` | string | **ID of the alert at the provider** (not Rapid's UUID). It is the value to use in `POST /chargeback-alert/get` and in the `POST /chargeback-alert/update/status` alias | | `merchant` | string | Merchant name | | `provider` | string | `ethoca` or `verifi-rdr` | | `descriptor` | string | Name on the cardholder's statement | | `transaction_date` | string | Transaction date (ISO 8601) | | `currency` | string | ISO 4217 | | `amount` | number \| null | Transaction amount | | `card_number` | string \| null | `BIN******LAST4`, or `null` when the provider sent neither BIN nor last digits | | `created_at` | string | When Rapid received the alert | | `arn` | string \| null | Acquirer Reference Number | | `authorization_code` | string \| null | Authorization code | | `issuer` | string \| null | Issuing bank | | `type` | string \| null | Alert type | | `global` | boolean | `true` for an international alert | | `reason_code`, `status_code`, `mcc`, `tier`, `caid` | string \| null | Provider fields, when present | | `installment_number`, `total_installment_count` | number \| null | Installments | There is no `event`, `status` or `expires_at` field in this format. Retries, timeout and logs are the same as in `v2`. --- # Authentication > How to validate the HMAC-SHA256 signature of Rapid webhooks, with replay protection and ready-made examples in Node.js, Python and PHP. Página: https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/ In the **`v2`** format, Rapid signs each webhook with **HMAC-SHA256** using your signing key (the `secret`). Rapid **generates** this key when you register the webhook and shows it **only once** in the dashboard, under **Settings › Webhook**; store it right away. There is no way to recover the key later: if you lose it, generate another one in **Settings › API** (the previous one stops working at that same moment). Your application must validate this signature before processing the payload; that is how you confirm the POST came from Rapid. > Accounts in the **`legacy`** format (migrated from the previous system) **do not receive a signature**: authentication is done through the `clientid`/`clientkey` headers, as before. Check which format you have in [Payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/). ## Signature header ``` X-Webhook-Signature: sha256= ``` Where `` is the HMAC-SHA256, in hexadecimal, of the string `${timestamp}.${raw_body}`, that is, the value of the `X-Webhook-Timestamp` header (seconds since the epoch), a dot, and the **raw request body** (the JSON received, byte for byte), using your signing key. The timestamp is part of the signature to provide **replay protection**: without it, a captured valid POST could be resent indefinitely with a correct signature. Reject requests whose `X-Webhook-Timestamp` falls outside a tolerance window (we recommend **5 minutes**). --- ## How to validate 1. Read the raw request body. **Do not parse it as JSON before validating**: the signature is computed over the exact bytes received. 2. Build `signed = timestamp + "." + raw_body`, compute `HMAC-SHA256(secret, signed)` and encode it in hexadecimal 3. Compare it with the value in `X-Webhook-Signature` (after the `sha256=` prefix) 4. Use a timing-safe comparison to avoid timing attacks ### Examples A complete endpoint in each language: it reads the raw body, validates the signature and the time window, and only then reads the JSON. It responds `401` to any delivery that does not pass, including one without the headers. The key comes from the `RAPID_WEBHOOK_SECRET` environment variable. **Node.js** ```javascript import crypto from 'node:crypto' import express from 'express' const TOLERANCE_SECONDS = 5 * 60 function validateSignature(rawBody, signature, timestamp, secret) { // 1. Tolerance window (anti-replay). Without it, a captured old POST // keeps passing forever. const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) return false // 2. Sign `timestamp.raw_body` (the body as received, not re-serialized). const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex') const a = Buffer.from(expected) const b = Buffer.from(signature ?? '') return a.length === b.length && crypto.timingSafeEqual(a, b) } const app = express() // express.raw, not express.json: the signature is computed over the raw body. app.post('/webhooks/rapid', express.raw({ type: 'application/json' }), (req, res) => { const rawBody = req.body.toString('utf8') const ok = validateSignature( rawBody, req.get('X-Webhook-Signature'), req.get('X-Webhook-Timestamp'), process.env.RAPID_WEBHOOK_SECRET, ) if (!ok) return res.status(401).end() const event = JSON.parse(rawBody) // Process the `event` (preferably in a queue) and respond quickly. res.status(200).end() }) app.listen(process.env.PORT ?? 3000) ``` **Python** ```python import hashlib import hmac import os import time from flask import Flask, request TOLERANCE_SECONDS = 5 * 60 def validate_signature(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool: # 1. Tolerance window (anti-replay). try: age = abs(int(time.time()) - int(timestamp)) except (TypeError, ValueError): return False if age > TOLERANCE_SECONDS or not signature: return False # 2. Sign `timestamp.raw_body` (the bytes received, not re-serialized). signed = timestamp.encode() + b"." + raw_body expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected.encode(), signature.encode()) app = Flask(__name__) @app.post("/webhooks/rapid") def rapid_webhook(): # get_data(), not get_json(): the signature is computed over the raw body. raw_body = request.get_data() ok = validate_signature( raw_body, request.headers.get("X-Webhook-Signature", ""), request.headers.get("X-Webhook-Timestamp", ""), os.environ["RAPID_WEBHOOK_SECRET"], ) if not ok: return "", 401 event = request.get_json() # Process the `event` (preferably in a queue) and respond quickly. return "", 200 ``` **PHP** ```php TOLERANCE_SECONDS) { return false; } // 2. Sign `timestamp.raw_body` (the bytes received, not re-serialized). $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret); return hash_equals($expected, $signature); } // php://input, not json_decode first: the signature is computed over the raw body. $rawBody = file_get_contents('php://input'); $ok = validateSignature( $rawBody, $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '', $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '', getenv('RAPID_WEBHOOK_SECRET') ); if (!$ok) { http_response_code(401); exit; } $event = json_decode($rawBody, true); // Process the $event (preferably in a queue) and respond quickly. http_response_code(200); ``` ## Signature checker Your validation does not match? Paste what you received here and compare it with the expected signature. --- ## Secret rotation You generate a new key yourself in the dashboard, under **Settings › API**. It is shown only once, and from then on webhooks go out signed with it, **with no overlap period**: whoever validates with the previous key starts rejecting. Change the key on your servers right after, preferably in a low-traffic window. Saving a new **URL** does not touch the key. --- ## Legacy headers In the `legacy` format, Rapid sends the `clientid` and `clientkey` headers and does **not** send a signature, exactly like the previous system. ``` clientid: clientkey: ``` In the `v2` format these headers **do not exist**: validating by `clientid`/`clientkey` would mean receiving the API access credential on every request (less secure than signing the payload), so delivery authentication is only `X-Webhook-Signature`. Accounts on `legacy` keep receiving the headers indefinitely, so existing integrations do not break. --- # Retries and logs > When Rapid tries to deliver the webhook again, at what intervals, what counts as success and how to read the delivery log. Página: https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/ ## Retry policy If your application responds with any code other than `2xx`, or does not respond within the timeout, Rapid considers the attempt a failure and sends the webhook again. There are **4 attempts** in total: the initial one plus 3 more, each waiting longer than the previous one. 1. **Right away** 1st attempt, as soon as the event is queued 2. **+5 min** 2nd attempt, 5 minutes after the previous failure (5 minutes from the start) 3. **+15 min** 3rd attempt (20 minutes from the start) 4. **+30 min** 4th and last attempt (50 minutes from the start) If the 4th also fails, the delivery is closed and the alert stays available in the dashboard for manual action (see [Manual redelivery](#manual-redelivery)). ## Timeout Each attempt has a **30-second timeout**. If your application does not respond within that time, the attempt is considered a failure. ## What counts as success Any **`2xx`** code: `200`, `201`, `202`, `204` and the rest of the range. The response body is optional. Any other code (including `4xx` and `5xx`) triggers a retry. ### Redirects Register the **final URL**. Rapid handles redirects like this: | Your URL's response | What happens | |---|---| | `301`, `302`, `303` | **Failure.** These codes turn the POST into a `GET` and drop the body, so following them would deliver nothing. The attempt goes into the retry flow and the log shows where your URL redirects to. | | `307`, `308` to the **same** domain | Followed, with the POST, body and headers intact. Up to 3 redirects in a row. | | `307`, `308` to **another** domain | **Failure.** The signed payload does not leave the domain you registered. | The common cases are the server appending `/` to the end of the path and switching domains, such as from `example.com` to `www.example.com` (which counts as another domain). In the delivery log, the reason starts with `[redirecionamento]`, along with the destination address. ## Address that is not public If the registered address does not resolve to a public internet address (it points to a local or private network, or the domain does not exist), the delivery **fails without any request** and goes into the normal retry flow. In the delivery log, the reason starts with `[destino]`. Fix the URL or the domain's DNS; see [Use a public internet address](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/#use-a-public-internet-address). If the reason starts with `[https]`, the registered URL does not use HTTPS and Rapid does not deliver in plain text: register an `https://` URL. If the reason starts with `[assinatura]`, the failure is on Rapid's side: the delivery does not go out unsigned, and it is retried automatically. There is nothing to do in your system. ## Blocked companies If the company is blocked for non-payment at Rapid, **the webhook flow keeps working normally**: alerts are still received from the providers, stored and delivered to the configured URL. The dashboard remains available. What the block cuts off is the **credential-based API** (read and write: `GET/PATCH /chargeback-alert/alerts`, transactions, merchants, legacy aliases), which starts responding `403 COMPANY_BLOCKED`, and the defense of new disputes. To stop receiving new alerts, the enrollment has to be deactivated directly at the provider (an action carried out by the Rapid team). ## Delivery logs Every attempt is recorded internally with: - Event delivered (`chargeback_alert.received`, `dispute.fraud.revoke_access`) - Date and time of the attempt - Destination URL - HTTP response code - Response body (truncated at 1000 characters) - Attempt number You see these records in the Rapid dashboard in two places: in **Settings › Webhook**, under **Delivery history**, the most recent deliveries; in **Activity**, **Webhooks** tab, the full history for debugging failures. ## Manual redelivery When every automatic attempt fails, the alert stays in the dashboard. To send it again, use the **Retry** button in the **Delivery history**, under **Settings › Webhook**. The redelivered event arrives as a new webhook with the same `alert_id`, which is why [idempotency](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/#idempotency) is essential. Redelivery from the dashboard applies to alerts. The `dispute.fraud.revoke_access` event has no manual redelivery. --- # Best practices > What a webhook endpoint needs to do: validate the signature, respond fast, be idempotent, use HTTPS and a public address. Página: https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/ ## Validate the signature first Compute and check `X-Webhook-Signature` **before** parsing the JSON or touching the database. Any request with an invalid signature must be discarded. See [Authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). ## Respond fast Return `200` or `204` as soon as possible, ideally in under 1 second. If you need to process the alert (e.g. update the database, call the gateway API), do it asynchronously after responding. The timeout is 30 seconds per attempt. Any delay eats into that budget and can trigger an unnecessary retry. ## Idempotency Rapid can send the same alert again: - When the previous attempt failed (automatic retry) - When someone on your team redelivers it from the dashboard - In rare cases of network failure between your server's response and the confirmation on Rapid's side Your processing must be **idempotent by `alert_id`**: 1. When you receive a webhook, check whether a record with that `alert_id` already exists in your system 2. If it does, return `200` immediately and do not process it again 3. If it does not, process and store it ## Ignore unknown fields The payload can evolve with new fields. Your deserialization must **tolerate extra fields** instead of failing. Never remove or change fields while processing. ## Return 200 even on a validation error If the payload arrives but you cannot process it (e.g. the merchant does not exist on your side), **still return 200**. Returning an error triggers a useless retry. Record the failure on your side (log, internal alert, error queue) and handle it offline. ## Always use HTTPS The webhook URL must be `https://`: the dashboard does not save an `http://` URL, and Rapid does not deliver over plain HTTP. The signature proves the request came from Rapid, but it does not encrypt the content, and the payload carries card data (BIN and last 4 digits) and purchase data. ## Use a public internet address The URL must point to a server reachable from the internet. Rapid does not deliver to a local or private network address: `localhost`, `127.0.0.1`, `10.x`, `172.16.x` to `172.31.x`, `192.168.x` and their IPv6 equivalents. The dashboard already rejects these addresses at registration, and a domain name that resolves to one of them fails at delivery time. To test the integration on your machine, expose the local server with an HTTPS tunnel (such as ngrok or Cloudflare Tunnel) and register the public address it generates. ## Protect the secret The `secret` used to validate `X-Webhook-Signature` must be treated as a sensitive credential: - Do not commit it to a repository - Do not log it - Store it in an environment variable or a secrets vault ## Monitoring We recommend instrumenting: - Rate of webhooks received (if it drops, something may be wrong at Rapid or on the network) - Rate of invalid signatures (if it rises, it may mean a secret rotation that was not propagated or a forgery attempt) - Processing latency (to make sure it stays below the 30s timeout) - Rate of retries delivered (points to problems on your side) --- # Overview > How Rapid records, at the moment they happen, the signals that back a sale: terms acceptance, device, access, delivery and usage. Página: https://doc.rapidchargeback.com/en/canais/capture/visao-geral/ Capture records, at the moment they happen, the signals that back a sale: the terms acceptance at checkout, the device used for the purchase, access to the product, delivery confirmation, use of the service. There are two channels for the same thing, and you can use both together: | Channel | Who calls | Authentication | |---|---|---| | **Browser** | the snippet in your checkout, or your own code | publishable token in the URL | | **Server** | your backend | Basic Auth, same as the other APIs | ## Flow in short 1. You enable the capture SDK in the dashboard and receive the merchant's publishable token 2. [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) in the checkout, or call the endpoint directly 3. For each relevant fact, send an event with the order's `order_ref` 4. Rapid correlates the event with the transaction and stores the record 5. If that transaction becomes a dispute, the records go into the defense ## How the event becomes evidence The event is **not** attached to anything when it arrives. It goes into a queue and only becomes evidence when it matches a real transaction of the merchant that owns the token, through the `order_ref` field. This has a practical consequence worth understanding: a leaked token produces noise, never data. Whoever has your publishable token can send events, but they die in the queue if they do not match a real order of yours. So: - sending an event before the transaction exists is normal, correlation happens later, within a window of about 68 hours - `order_ref` must be the same identifier you use in the transaction (`external_id`) - the success response is `202 Accepted`, not `201`: Rapid accepted the event, processing is asynchronous - a wrong `order_ref` does not return an error. The event is accepted and silently discarded later ## Difference between capture and evidence | | Capture | [Evidence](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/) | |---|---|---| | When to use | at the moment of the fact, without knowing whether it becomes a dispute | when you already have the transaction at Rapid | | Reference | `order_ref` (your identifier) | `transaction_id` or `transaction_ref` | | Transaction must exist | no | yes, otherwise `404` | | Payload fields | closed list | free format, 32 KB cap | | Response | `202`, asynchronous | `201`, already stored | ## Authentication - **Browser:** the token goes in the URL (`/capture/{token}/events`). It is publishable and can stay in the HTML. - **Server:** Basic Auth with `client_id:client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). The capture token is generated in the dashboard, under **Settings › Integrations**, when you enable the merchant's capture SDK. ## Endpoints | Endpoint | Channel | What it does | |---|---|---| | [`POST /capture/{token}/events`](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) | browser | Sends an event from the checkout | | [`POST /capture/events`](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) | server | Sends an event from your backend | | [`GET /capture/{token}/config`](https://doc.rapidchargeback.com/en/canais/capture/config-do-snippet/) | browser | Configuration the snippet reads when it loads | ## Next steps - [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) - [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/) - [Send an event from the browser](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) - [Send an event from the server](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) --- # Install the snippet > The snippet is the shortest way to capture evidence: two tags in your checkout and one call per event. It talks to the browser channel for you. Página: https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/ The snippet is the shortest way to capture evidence: two tags in your checkout and one call per event. It talks to the [browser channel](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) for you. If your checkout is rendered on the server, or if you prefer not to load a third-party script, skip this page and call the endpoint directly. ## Where to get the token In the dashboard, under **Settings › Integrations**, on the capture SDK card. Enable it and the dashboard shows the ready-made snippet, already with the token of the selected merchant. The card shows up for those who have the dispute product active. The token is **publishable**: it stays in your store's HTML and needs no protection. With it you can only send events, never read data. An event that does not match one of your orders is discarded. ## Installation ```html ``` Two things matter in the order above: 1. `init` has to come after the script loads, which is why the tag has neither `async` nor `defer` 2. `init` already starts identifying the device, so call it as early as possible on the page, not only at payment time The script is about 5 KB, has no dependencies and does not block the page. ## Calls ```js // Purchase completed. Also captures the device data. rapid('checkout', { order_ref: 'TXN-2026-001' }) // Customer accepted the terms. rapid('terms', { order_ref: 'TXN-2026-001' }) // Customer accessed the product. rapid('track', 'access_log', { order_ref: 'TXN-2026-001' }) // Customer used the product or service. rapid('track', 'usage_log', { order_ref: 'TXN-2026-001' }) ``` | Call | Event generated | Payload the snippet builds | |---|---|---| | `rapid('checkout', …)` | `checkout` | device identification | | `rapid('terms', …)` | `terms_acceptance` | the page URL | | `rapid('track', 'access_log', …)` | `access_log` | the page URL | | `rapid('track', 'usage_log', …)` | `usage_log` | the page URL | `order_ref` is required in all of them. It is the order identifier in your system, the same one you send in `external_id` when creating the transaction. See [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/). A call without `order_ref` is ignored: it generates neither an event nor a visible error. ## Fire and forget The snippet never breaks the checkout, and that has a cost worth knowing: - every call is silent. It throws no exception, returns no promise and does not read the response - validation errors (wrong type, `order_ref` too long) do not show up in the console - network failures are not retried in the browser To **test** the integration, call the [browser endpoint](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) directly with curl, where you see the status and the message. Once validated, the snippet does the same in production. Sending uses `navigator.sendBeacon` when available, with `fetch` in `keepalive` mode as the alternative. Both survive navigation to the next page, so the checkout event is not lost in the payment redirect. ## Device identification On `checkout`, the snippet identifies the device before sending the event. It tries Fingerprint, with a 2-second cap, and falls back to its own identification if it cannot (an identifier in `localStorage` plus a hash of browser attributes). The result goes in the payload's `fp` field: | `fp` | Meaning | |---|---| | `pro` | Fingerprint identification, validated on the server side | | `fallback` | the snippet's own identification | The difference is practical: only the validated identification fills in the transaction's device and IP and generates the verification record. See [The `checkout` event](https://doc.rapidchargeback.com/en/canais/capture/payload/#the-checkout-event). If your site has a strict CSP, Fingerprint is loaded from `https://fpjscdn.net`. If it is blocked, capture keeps working in `fallback` mode. ## How to know it is working The same dashboard card shows whether the account has already received events and when the last one was. After installing, make a test purchase and check there. ## Next steps - [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/) - [Send an event from the browser](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) - [Send an event from the server](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) --- # Send an event from the browser > Records a capture event using the publishable token. It is the endpoint the snippet calls, and you can call it directly if you prefer not to load the script. Página: https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/ Records a capture event using the publishable token. It is the endpoint the [snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) calls, and you can call it directly if you prefer not to load the script. ## Endpoint ``` POST https://api.rapidchargeback.com/api/v1/capture/{token}/events ``` ## Authentication None. The publishable token in the URL identifies the merchant. ``` Content-Type: application/json ``` The token is not a secret and can stay in your store's HTML. It only allows writing, and the event only becomes evidence if it matches a real order of yours. ## URL parameters | Field | Type | Description | |---|---|---| | `token` | string | Merchant's publishable token, generated in the dashboard | ## Request body | Field | Type | Description | Required | |---|---|---|---| | `type` | string (enum) | `checkout`, `terms_acceptance`, `access_log` or `usage_log` | Yes | | `order_ref` | string | Order identifier in your system, up to 100 characters | Yes | | `event_id` | string | Event identifier, generated by you, up to 64 characters | Yes | | `payload` | object | Data about the fact, with a closed list of fields | No | The full reference of accepted values is in [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/). Two points specific to this channel: - `event_id` is required here. Generate a UUID per event - `captured_at` sent in the body is ignored. Rapid records the arrival time. To send the time of the fact, use the [server channel](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) The `delivery_confirmation`, `scan` and `delivery_gps` types do not exist in this channel: they are facts of your operation, not of the buyer's browser. ## Rate limit 120 requests per minute, per source IP. That is plenty for a real checkout and tight for anyone using the token outside your site. ## Validations | Rule | Response | |---|---| | Unknown or inactive token, or token without a merchant | `404 NOT_FOUND` | | `type` missing, or not one of the four accepted in this channel | `422 VALIDATION_ERROR`: *invalid_type* | | `order_ref` missing, empty or over 100 characters | `422 VALIDATION_ERROR`: *invalid_order_ref* | | `event_id` missing, empty or over 64 characters | `422 VALIDATION_ERROR`: *invalid_event_id* | | `payload` text field over 512 characters | `422 VALIDATION_ERROR`: *payload_too_large* | | Over 120 requests per minute | `429 Too Many Requests` | There is no error for a nonexistent `order_ref`. The event is accepted, stays in the queue and is discarded if no transaction matches within the correlation window. See [Correlation window](https://doc.rapidchargeback.com/en/canais/capture/payload/#correlation-window). ## Request example ```bash curl -X POST https://api.rapidchargeback.com/api/v1/capture/your_publishable_token/events \ -H "Content-Type: application/json" \ -d '{ "type": "terms_acceptance", "order_ref": "TXN-2026-001", "event_id": "019b0000-1111-7000-8000-000000000001", "payload": { "url": "https://loja.exemplo.com/checkout" } }' ``` Checkout event with device identification: ```bash curl -X POST https://api.rapidchargeback.com/api/v1/capture/your_publishable_token/events \ -H "Content-Type: application/json" \ -d '{ "type": "checkout", "order_ref": "TXN-2026-001", "event_id": "019b0000-1111-7000-8000-000000000002", "payload": { "device_id": "8kJ2mQvR1nZxYb0T", "device_fingerprint": "8kJ2mQvR1nZxYb0T", "fp": "pro", "fp_request_id": "1712059781234.Xk9Lp2" } }' ``` ## Response examples ### Success (202) ```json { "accepted": true } ``` `202` means the event entered the queue, not that it has already become evidence. Correlation with the transaction happens later. ### Error: unknown token (404) ```json { "error": { "code": "NOT_FOUND" } } ``` ### Error: type not available in this channel (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "invalid_type" } } ``` ### Error: missing `event_id` (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "invalid_event_id" } } ``` ### Error: payload field too long (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "payload_too_large" } } ``` ## Next steps - [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) - [Send an event from the server](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) - [Snippet configuration](https://doc.rapidchargeback.com/en/canais/capture/config-do-snippet/) --- # Send an event from the server > Record capture events from your backend: confirmed delivery, tracking, service usage and backdated events. Página: https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/ Records a capture event from your backend, with the same credentials as the other APIs. It is the channel for facts that do not happen in the browser: confirmed delivery, tracking scan, delivery coordinate, and for any event you need to resend or date. ## Endpoint ``` POST https://api.rapidchargeback.com/api/v1/capture/events ``` ## Authentication Basic Auth with `client_id:client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` Do not use the publishable token here. The company comes from the credential, which is why this channel can write device data without the validation required in the browser. ## Request body | Field | Type | Description | Required | |---|---|---|---| | `merchant_id` | string (UUID) | Merchant the event belongs to, within your company (see [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/)) | Yes | | `type` | string (enum) | The four browser types plus `delivery_confirmation`, `scan` and `delivery_gps` | Yes | | `order_ref` | string | Order identifier in your system, up to 100 characters | Yes | | `event_id` | string | Event identifier, up to 64 characters. If omitted, Rapid generates one | No | | `payload` | object | Data about the fact, with a closed list of fields | No | | `captured_at` | string (ISO 8601) | When the fact happened. If omitted, the time of the call is used | No | The full reference of accepted values is in [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/). Three differences from the browser channel: - `merchant_id` is required, because the credential identifies the company and not the merchant - `captured_at` is respected, so a batch routine can send the real time of each fact - `event_id` is optional, but send your own: it is what makes resending safe ## Idempotency Resending the same call with the same `event_id` does not duplicate the evidence. That is the expected behavior on a retry after a timeout or a lost response. If you omit `event_id`, each call generates a new identifier, and the same call repeated becomes two records. ## Validations | Rule | Response | |---|---| | Missing or invalid credential | `401 UNAUTHORIZED` | | `merchant_id` missing | `422 VALIDATION_ERROR`: *merchant_id required* | | `type` missing or outside the enum | `422 VALIDATION_ERROR`: *invalid_type* | | `order_ref` missing, empty or over 100 characters | `422 VALIDATION_ERROR`: *invalid_order_ref* | | `event_id` over 64 characters | `422 VALIDATION_ERROR`: *invalid_event_id* | | `payload` text field over 512 characters | `422 VALIDATION_ERROR`: *payload_too_large* | A `merchant_id` from another company does not cause an error in the call: the event is accepted and discarded at correlation, because the transaction is looked up within your company. The same goes for a nonexistent `order_ref`. ## Request example Confirmed delivery: ```bash curl -X POST https://api.rapidchargeback.com/api/v1/capture/events \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "merchant_id": "00000000-0000-0000-0000-000000000001", "type": "delivery_confirmation", "order_ref": "TXN-2026-001", "event_id": "entrega-TXN-2026-001", "payload": { "carrier": "correios", "tracking": "AA123456789BR" }, "captured_at": "2026-04-03T11:20:00Z" }' ``` Delivery coordinate: ```bash curl -X POST https://api.rapidchargeback.com/api/v1/capture/events \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "merchant_id": "00000000-0000-0000-0000-000000000001", "type": "delivery_gps", "order_ref": "TXN-2026-001", "event_id": "gps-TXN-2026-001", "payload": { "lat": -23.5614, "lng": -46.6559 }, "captured_at": "2026-04-03T11:20:00Z" }' ``` ## Response examples ### Success (202) ```json { "accepted": true } ``` `202` means the event entered the queue. Correlation with the transaction happens later, through `order_ref`. ### Error: missing credential (401) ```json { "error": { "code": "UNAUTHORIZED", "message": "Missing or invalid Authorization header" } } ``` ### Error: missing `merchant_id` (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "merchant_id required" } } ``` ### Error: invalid type (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "invalid_type" } } ``` ## Capture or evidence Both channels record a fact on a transaction, and the choice is simple: | Situation | Use | |---|---| | The transaction is already at Rapid and you have its UUID | [`POST /evidence`](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/) | | You only have your own order identifier, or the fact happens before the transaction is sent | this endpoint | | The fact needs a field outside the closed payload list | [`POST /evidence`](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/), which accepts a free-form payload | ## Next steps - [Types and payload](https://doc.rapidchargeback.com/en/canais/capture/payload/) - [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) - [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/) --- # Types and payload > Reference of the values accepted in the two capture channels. Applies to POST /capture/{token}/events and POST /capture/events. Página: https://doc.rapidchargeback.com/en/canais/capture/payload/ Reference of the values accepted in the two capture channels. Applies to [`POST /capture/{token}/events`](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) and [`POST /capture/events`](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/). ## Event fields | Field | Type | Limit | Required | |---|---|---|---| | `type` | string (enum) | see table below | Yes | | `order_ref` | string | 100 characters | Yes | | `event_id` | string | 64 characters | Yes in the browser, optional on the server | | `payload` | object | see accepted fields | No | | `captured_at` | string (ISO 8601) | - | No, and only the server channel uses it | | `merchant_id` | string (UUID) | - | Server channel only | ### `order_ref` It is the order identifier in **your** system, and it is the only thing that links the event to a transaction. Rapid looks, within your company and the given merchant, for: 1. a transaction with `external_id` equal to `order_ref` 2. if not found, a transaction with `order_number` equal to `order_ref` Always send the same value you used in `external_id` when [creating the transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/). A wrong `order_ref` does not cause an error in the call: the event is accepted, finds no transaction and is discarded after the correlation window. ### `event_id` Event identifier, generated by you. It works as an idempotency key: resending the same `event_id` for the same merchant does not create duplicate evidence. In the browser channel it is **required** (the snippet generates a UUID per event). In the server channel, if you omit it, Rapid generates one. Send your own whenever you might resend the same call, that is what guarantees idempotency. Do not use `:` in `event_id`. The character is removed before internal use, which would make two different ids collide. ### `captured_at` When the fact happened, in ISO 8601. | Channel | Behavior | |---|---| | Browser | ignored. Rapid records the time the event arrived | | Server | used as sent. If omitted, the time of the call is used | If the fact happened before the call (a nightly batch process, for example), use the server channel and send `captured_at`. ## `type` values | Value | What it records | Browser | Server | |---|---|---|---| | `checkout` | Purchase completion, with device data | Yes | Yes | | `terms_acceptance` | Acceptance of terms, policy or contract | Yes | Yes | | `access_log` | Access to the product or the logged-in area | Yes | Yes | | `usage_log` | Actual use of the product or service | Yes | Yes | | `delivery_confirmation` | Delivery confirmed | No | Yes | | `scan` | Tracking code scan in transit | No | Yes | | `delivery_gps` | Delivery coordinate | No | Yes | The last three exist only in the server channel: they are facts of your operation, not of the buyer's browser. Using them in the browser channel returns `422 invalid_type`. ## `payload` fields `payload` has a **closed list** of fields. A key outside the list is silently discarded, with no error, so check the spelling. | Field | Type | Use in | |---|---|---| | `device_id` | string | `checkout` | | `device_fingerprint` | string | `checkout` | | `fp` | string | `checkout`, indicates the origin of the identification (`pro` or `fallback`) | | `fp_request_id` | string | `checkout`, required to validate the identification | | `url` | string | `terms_acceptance`, `access_log`, `usage_log` | | `note` | string | any type | | `carrier` | string | `delivery_confirmation`, `scan` | | `tracking` | string | `delivery_confirmation`, `scan` | | `lat` | number | `delivery_gps` | | `lng` | number | `delivery_gps` | Rules: - scalar values only (string, number, boolean). Objects and lists are discarded - a string over **512 characters** returns `422 payload_too_large` - a missing or empty `payload` is accepted ## The `checkout` event `checkout` is the only type that writes data directly to the transaction, and not just as an attached record. It fills in the transaction's device and IP **only when those fields are empty**: an earlier capture is never overwritten. What each channel can write: | Transaction data | Browser | Server | |---|---|---| | `device_id` | does not write | writes | | `device_fingerprint` | only with validated identification | writes | | `ip_address` | only with validated identification | writes | The difference exists because the browser token is publishable: anyone who has it can send events. Unvalidated data never touches the fields that back the defense. **Validated identification** means: you sent `fp_request_id` and `device_id` in the payload, and Rapid confirmed the pair against Fingerprint on the server side. In that case the event also generates a device verification record, with bot, VPN and incognito window signals. Without `fp_request_id`, the event still counts as a record, just without the device part. Each `fp_request_id` is valid for **one** transaction. The same pair reused in another order is treated as a replay and ignored. ## Correlation window The event can arrive before the transaction exists. It stays in the queue and is retried for about **68 hours**, at increasing intervals. Once the window passes with no matching transaction, the event is discarded. In practice: sending `checkout` at the exact moment of the purchase works, even if your routine only sends the transaction to Rapid hours later. ## Next steps - [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) - [Send an event from the browser](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) - [Send an event from the server](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-servidor/) --- # Snippet configuration > Endpoint that returns the account's identification configuration, read by the snippet when it loads. Only needed if you write your own collector. Página: https://doc.rapidchargeback.com/en/canais/capture/config-do-snippet/ Returns the account's identification configuration. The [snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) calls this endpoint when it loads, before the first event. You only need it if you write your own collector in the browser. ## Endpoint ``` GET https://api.rapidchargeback.com/api/v1/capture/{token}/config ``` ## Authentication None. The publishable token in the URL identifies the merchant. ## URL parameters | Field | Type | Description | |---|---|---| | `token` | string | Merchant's publishable token, generated in the dashboard | ## Validations | Rule | Response | |---|---| | Unknown or inactive token, or token without a merchant | `404 NOT_FOUND` | ## Cache The response comes with `cache-control: public, max-age=300`. Respect the cache: the configuration rarely changes and the snippet does not need to fetch it on every page. ## Request example ```bash curl https://api.rapidchargeback.com/api/v1/capture/your_publishable_token/config ``` ## Response examples ### Success (200), with Fingerprint active ```json { "data": { "fp_public_key": "pk_exemplo_123456", "fp_region": "us" } } ``` | Field | Description | |---|---| | `fp_public_key` | Fingerprint public key to use in the browser. `null` when identification is not active | | `fp_region` | Fingerprint region: `us`, `eu` or `ap` | With the key, the collector loads the Fingerprint agent and gets two values: the visitor identifier and the request identifier. Send them in the `checkout` event as `device_id` and `fp_request_id`, and Rapid validates the pair on the server side. See [The `checkout` event](https://doc.rapidchargeback.com/en/canais/capture/payload/#the-checkout-event). ### Success (200), without Fingerprint active ```json { "data": { "fp_public_key": null, "fp_region": "us" } } ``` A null `fp_public_key` does not prevent capture. The event is still recorded, just without the validated device identification part. ### Error: unknown token (404) ```json { "error": { "code": "NOT_FOUND" } } ``` ## Next steps - [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/) - [Send an event from the browser](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/) --- # Overview > The Evidence API attaches to an already sent transaction the facts that back the defense: acceptance, access, delivery, usage and conversation with the customer. Página: https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/ The Evidence API records, on a transaction already sent to Rapid, the facts that back a defense: terms acceptance, access to the product, delivery confirmation, usage and communication with the customer. It is an **inbound** channel: you call Rapid. The record stays attached to the transaction and is used to build the defense when that transaction becomes a dispute. ## What it is not - **It is not file upload.** Each evidence item is a JSON record with a 32 KB cap. PDF receipts, screenshots and contracts go in the dashboard's evidence library, not here. - **It does not create transactions.** The transaction must exist beforehand, sent through the [Transactions API](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/). If it does not exist, the call returns `404 TRANSACTION_NOT_FOUND`. ## Flow in short 1. You send the sale transaction through the Transactions API 2. Something that backs the sale happens in your system: the customer accepted the terms, accessed the product, received the delivery 3. You record that fact with `POST /evidence`, pointing to the transaction 4. If that transaction becomes a dispute, Rapid uses the records in the defense ## Authentication Basic Auth with `client_id:client_secret`, same as the other channels. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ## Endpoints | Endpoint | What it does | |---|---| | [`POST /evidence`](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/) | Records evidence on a transaction | | [`GET /evidence`](https://doc.rapidchargeback.com/en/canais/evidence/consultar-evidencias/) | Lists the evidence of a transaction | ## Next steps - [Record evidence](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/) - [List evidence](https://doc.rapidchargeback.com/en/canais/evidence/consultar-evidencias/) --- # Record evidence > Attaches an evidence record to a transaction already sent to Rapid. Página: https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/ Attaches an evidence record to a transaction already sent to Rapid. ## Endpoint ``` POST https://api.rapidchargeback.com/api/v1/evidence ``` ## Authentication Basic Auth with `client_id:client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` ## Request body | Field | Type | Description | Required | |---|---|---|---| | `transaction_id` | string (UUID) | Transaction ID at Rapid, returned when you created it | Yes, or `transaction_ref` | | `transaction_ref` | object | Alternative to `transaction_id`: points to the transaction by **your** identifier. See below | Yes, or `transaction_id` | | `type` | string (enum) | Nature of the recorded fact. Accepted values below | Yes | | `payload` | object | The data about the fact. Free format, 32 KB cap | Yes | | `captured_at` | string (ISO 8601 with offset) | When the fact happened in your system, not when you send it | Yes | Send **one** of the two: `transaction_id` or `transaction_ref`. If both are missing, the response is `422`. ### `transaction_ref` field | Field | Type | Description | Required | |---|---|---|---| | `external_source` | string | The same source you used when creating the transaction (e.g. `"shopify"`) | Yes | | `external_id` | string | The same ID from your system you used when creating the transaction | Yes | ### `type` values | Value | When to use | |---|---| | `terms_acceptance` | Customer accepted terms, policy or contract | | `access_log` | Customer accessed the product or the logged-in area | | `delivery_confirmation` | Delivery confirmed | | `usage_log` | Actual use of the product or service | | `communication` | Exchange with the customer (email, chat, ticket) | | `other` | Any fact that does not fit the previous ones | ### `payload` field Free format: you choose the keys. The only rule is the **32 KB cap** on the serialized JSON: evidence is a record, not a file. Suggestions by type, not required: ```json // terms_acceptance { "accepted_at": "2026-04-01T15:29:40Z", "terms_version": "v3.2", "ip": "203.0.113.10" } // access_log { "first_access_at": "2026-04-01T16:02:11Z", "sessions": 3 } // delivery_confirmation { "carrier": "correios", "tracking": "AA123456789BR", "delivered_at": "2026-04-03T11:20:00Z" } ``` ## Validations | Rule | Response | |---|---| | Neither `transaction_id` nor `transaction_ref` | `422 VALIDATION_ERROR`: *transaction_id or transaction_ref is required* | | `type` outside the enum | `422 VALIDATION_ERROR` with the list of accepted values | | `captured_at` without a time zone offset | `422 VALIDATION_ERROR`: *Invalid datetime* | | Serialized `payload` over 32 KB | `422 VALIDATION_ERROR`: *payload exceeds 32KB: evidence is a record, not a file* | | Transaction does not exist, or is not from your company | `404 TRANSACTION_NOT_FOUND` | | Missing or invalid credential | `401 UNAUTHORIZED` | The transaction is always resolved **within your company**. A transaction ID from another company returns `404`, never `403`: we do not confirm the existence of someone else's data. ## Request example ```bash curl -X POST https://api.rapidchargeback.com/api/v1/evidence \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "transaction_id": "00000000-0000-0000-0000-000000000042", "type": "terms_acceptance", "payload": { "accepted_at": "2026-04-01T15:29:40Z", "terms_version": "v3.2", "ip": "203.0.113.10" }, "captured_at": "2026-04-01T15:29:41Z" }' ``` By your own identifier, without storing Rapid's UUID: ```bash curl -X POST https://api.rapidchargeback.com/api/v1/evidence \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "transaction_ref": { "external_source": "shopify", "external_id": "TXN-2026-001" }, "type": "access_log", "payload": { "first_access_at": "2026-04-01T16:02:11Z", "sessions": 3 }, "captured_at": "2026-04-01T16:02:12Z" }' ``` ## Response examples ### Success (201) ```json { "data": { "id": "00000000-0000-0000-0000-0000000000e1", "transaction_id": "00000000-0000-0000-0000-000000000042", "type": "terms_acceptance" } } ``` The returned `id` is the evidence's. Store it only if you are going to reference it; to list, the key is the transaction. ### Error: missing transaction reference (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "transaction_id or transaction_ref is required" } } ``` ### Error: invalid `type` (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "Invalid enum value. Expected 'terms_acceptance' | 'access_log' | 'delivery_confirmation' | 'usage_log' | 'communication' | 'other', received 'nao_existe'" } } ``` ### Error: transaction not found (404) ```json { "error": { "code": "TRANSACTION_NOT_FOUND", "message": "Transaction not found" } } ``` ### Error: payload over the cap (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "payload exceeds 32KB: evidence is a record, not a file" } } ``` ## Next steps - [List evidence](https://doc.rapidchargeback.com/en/canais/evidence/consultar-evidencias/) - [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/) --- # List evidence > Lists the evidence recorded on a transaction, from oldest to newest. Página: https://doc.rapidchargeback.com/en/canais/evidence/consultar-evidencias/ Lists the evidence recorded on a transaction, from oldest to newest. ## Endpoint ``` GET https://api.rapidchargeback.com/api/v1/evidence?transaction_id= ``` ## Authentication Basic Auth with `client_id:client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ## Query params | Param | Type | Description | Required | |---|---|---|---| | `transaction_id` | string (UUID) | Transaction whose evidence you want to see | Yes | Unlike the `POST`, there is **no** lookup by `transaction_ref` here: the query is by Rapid's UUID only. ## Validations | Rule | Response | |---|---| | `transaction_id` missing | `422 VALIDATION_ERROR`: *transaction_id is required* | | Missing or invalid credential | `401 UNAUTHORIZED` | A transaction from another company, or a nonexistent one, returns an **empty list**, because the query is filtered by your company; it does not return an error. ## Request example ```bash curl "https://api.rapidchargeback.com/api/v1/evidence?transaction_id=00000000-0000-0000-0000-000000000042" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` ## Response examples ### Success (200) ```json { "data": [ { "id": "00000000-0000-0000-0000-0000000000e1", "type": "terms_acceptance", "payload": { "ip": "203.0.113.10", "accepted_at": "2026-04-01T15:29:40Z", "terms_version": "v3.2" }, "captured_at": "2026-04-01T15:29:41.000Z", "created_at": "2026-04-01T15:32:36.027Z" } ] } ``` | Field | Description | |---|---| | `id` | Evidence ID | | `type` | The type given in the record | | `payload` | The object you sent, as you sent it. Key order is not preserved | | `captured_at` | When the fact happened, as you reported it | | `created_at` | When Rapid received the record | ### No evidence (200) ```json { "data": [] } ``` ### Error: missing `transaction_id` (422) ```json { "error": { "code": "VALIDATION_ERROR", "message": "transaction_id is required" } } ``` ## Next steps - [Record evidence](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/) - [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/) --- # Overview > The status callback: the answer your application sends back to Rapid after analyzing an alert, and when it is mandatory. Página: https://doc.rapidchargeback.com/en/callback/visao-geral/ After receiving an alert by webhook, your application returns a **status** to Rapid with the result of its analysis: - **Ethoca (Mastercard)**: mandatory within 24h. The answer goes back to Ethoca and can prevent the dispute from becoming a chargeback. - **Verifi RDR (Visa)**: optional. The refund was already made automatically; the status is only for your internal organization. ## Summary flow 1. Your application receives the alert by [webhook](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/) 2. You analyze the transaction in your system 3. You call `PATCH /chargeback-alert/alerts/:id/status` with one of the three valid statuses 4. Rapid processes and stores it and (for Ethoca) forwards it to the provider Alternatively, you can update the status directly in the Rapid dashboard (useful for manual operations). ## Authentication The callback uses **Basic Auth** with `client_id`/`client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ## Valid statuses There are three: `notfound`, `account_suspended` and `other`. What each one means, the deadlines for each card network and the allowed transitions are in [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/). ## List alerts If your integration needs to look up alerts (e.g. confirm whether an alert with a given `provider_alert_id` arrived, or page through history), use the listing endpoint: ``` GET /api/v1/chargeback-alert/alerts ``` It accepts filters by `status`, `provider_alert_id`, date range, BIN and card last digits. Useful when you only have the provider's ID (Ethoca/Verifi) and need to find Rapid's internal ID to update the status. ## Next steps - [List alerts](https://doc.rapidchargeback.com/en/callback/consultar-alertas/): list alerts with filters (including search by your `provider_alert_id`). - [Update status](https://doc.rapidchargeback.com/en/callback/atualizar-status/): full spec of the callback endpoint. --- # Update status > Updates the status of an alert received by webhook. Página: https://doc.rapidchargeback.com/en/callback/atualizar-status/ Updates the status of an alert received by webhook. ## Endpoint ``` PATCH https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts/:id/status ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) Content-Type: application/json ``` --- ## URL parameter | Parameter | Type | Description | |---|---|---| | `id` | string (UUID) | `alert_id` received in the webhook payload | --- ## Request body | Field | Type | Required | Description | |---|---|---|---| | `status` | string | Yes | One of three values: `notfound`, `account_suspended`, `other` | --- ## Validations - `status` must be exactly one of the three valid values; anything else returns `422` - The alert must belong to your company; otherwise it returns `404` - Final statuses (`notfound`, `account_suspended`) cannot be changed once sent; trying returns an error - The `expired` status is set automatically by the system after 24h with no response (Ethoca) and is not sent by the customer - The only transition allowed after the initial response is `other → account_suspended`, available for up to 6 days (Ethoca) See [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/) for the full detail. --- ## Request example The credentials come from environment variables (`RAPID_CLIENT_ID` and `RAPID_CLIENT_SECRET`), never written in the code. **cURL** ```bash curl -X PATCH https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts/00000000-0000-0000-0000-000000000099/status \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "status": "account_suspended" }' ``` **Node.js** ```javascript const credentials = Buffer.from(`${process.env.RAPID_CLIENT_ID}:${process.env.RAPID_CLIENT_SECRET}`).toString('base64') const alertId = '00000000-0000-0000-0000-000000000099' const response = await fetch(`https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts/${alertId}/status`, { method: 'PATCH', headers: { Authorization: `Basic ${credentials}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'account_suspended' }), }) const body = await response.json() if (!response.ok) throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`) console.log('Status updated:', body.data.status) ``` **Python** ```python import os import requests alert_id = "00000000-0000-0000-0000-000000000099" response = requests.patch( f"https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts/{alert_id}/status", auth=(os.environ["RAPID_CLIENT_ID"], os.environ["RAPID_CLIENT_SECRET"]), json={"status": "account_suspended"}, timeout=30, ) body = response.json() if not response.ok: raise RuntimeError(f"{response.status_code} {body['error']['code']}: {body['error']['message']}") print("Status updated:", body["data"]["status"]) ``` **PHP** ```php 'PATCH', CURLOPT_USERPWD => getenv('RAPID_CLIENT_ID') . ':' . getenv('RAPID_CLIENT_SECRET'), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode(['status' => 'account_suspended']), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $raw = curl_exec($ch); if ($raw === false) { throw new RuntimeException('Network error: ' . curl_error($ch)); } $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $body = json_decode($raw, true); if ($status >= 400) { throw new RuntimeException("$status {$body['error']['code']}: {$body['error']['message']}"); } echo 'Status updated: ', $body['data']['status'], PHP_EOL; ``` --- ## Response examples ### Success ```json { "data": { "id": "00000000-0000-0000-0000-000000000099", "status": "account_suspended", "updated_at": "2026-04-14T14:30:00.000Z" } } ``` ### Alert not found ```json { "error": { "code": "ALERT_NOT_FOUND", "message": "Alert not found" } } ``` ### Company blocked ```json { "error": { "code": "COMPANY_BLOCKED", "message": "Company is blocked" } } ``` ### Status outside the valid values When the `status` sent is not one of the three accepted values, body validation fails before processing and returns `VALIDATION_ERROR` (HTTP 422): ```json { "error": { "code": "VALIDATION_ERROR", "message": "Invalid enum value. Expected 'notfound' | 'account_suspended' | 'other', received 'foo'" } } ``` ### Transition not allowed When the `status` is valid but the transition is not allowed (deadline, expiration or status already final), the code is `ALERT_INVALID_STATUS` (HTTP 422). The message says which of the three cases happened: ```json { "error": { "code": "ALERT_INVALID_STATUS", "message": "Alert has expired" } } ``` ```json { "error": { "code": "ALERT_INVALID_STATUS", "message": "Response deadline has passed" } } ``` ```json { "error": { "code": "ALERT_INVALID_STATUS", "message": "Alert already has a definitive status" } } ``` These three only happen for Ethoca alerts; Verifi RDR has no deadline rules (see [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/)). --- ## Customers from the previous system If you integrated with the previous version of the platform, the old endpoint is still available as an alias: ``` POST https://api.rapidchargeback.com/chargeback-alert/update/status ``` This alias accepts the body in the old format `{ "alert_id": "...", "status": "..." }` and both Basic Auth and the `clientid`/`clientkey` headers. As in the previous system, **`alert_id` is the alert ID at the provider** (the same `alert_id` you receive in the `legacy` webhook format and use in `POST /chargeback-alert/get`); Rapid's UUID is also accepted. The response keeps the old format `{ "success": true, "message": "...", "status": "..." }` and adds `data` with the object of the canonical endpoint. Errors follow the new envelope (`422 VALIDATION_ERROR` / `ALERT_INVALID_STATUS`, `404 ALERT_NOT_FOUND`). **Recommendation:** migrate to `PATCH /chargeback-alert/alerts/:id/status`. The alias will be removed in the future, once there are no more calls to it. --- # List alerts > Lists your company's chargeback alerts, with optional filters and pagination. Página: https://doc.rapidchargeback.com/en/callback/consultar-alertas/ Lists your company's chargeback alerts, with optional filters and pagination. ## Endpoint ``` GET https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts ``` ## Authentication ``` Authorization: Basic base64(client_id:client_secret) ``` --- ## Query params All optional. Combine the ones you need. | Param | Type | Default | Description | |---|---|---|---| | `page` | int (≥ 1) | `1` | Pagination page | | `per_page` | int (1–100) | `20` | Items per page | | `status` | enum | - | `pending`, `notfound`, `account_suspended`, `other`, `expired` | | `provider_alert_id` | string | - | Alert ID in the provider's system (Ethoca/Verifi). Unique within each provider; usually returns 0 or 1 item | | `date_from` | YYYY-MM-DD | - | Filters by `received_at >= date_from` (00:00:00) | | `date_to` | YYYY-MM-DD | - | Filters by `received_at <= date_to` (23:59:59) | | `card_bin` | string (6-8 digits) | - | Card BIN | | `card_last4` | string (4 digits) | - | Card last digits | --- ## Search by your own ID (the provider's) Alerts have two IDs: - **`id`**: UUID that Rapid generates when it receives the alert. It is what you need to update the status (`PATCH /chargeback-alert/alerts/:id/status`). - **`provider_alert_id`**: ID generated by Ethoca or Verifi. It is what you probably already have stored in your systems, from the provider's original payload. If you only have the `provider_alert_id` and need to find our `id` (or the alert data), use this endpoint with the filter: ```bash curl -G "https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts" \ --data-urlencode "provider_alert_id=ALERT-ETHOCA-12345" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` The `provider_alert_id` is unique within each provider, so the response will usually have 1 item in `data`. For a stable, globally unique key, use the `id` (UUID) in the response. --- ## Examples ### List the latest pending alerts ```bash curl -G "https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts" \ --data-urlencode "status=pending" \ --data-urlencode "per_page=50" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` ### Alerts from the last 7 days ```bash curl -G "https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts" \ --data-urlencode "date_from=2026-04-16" \ --data-urlencode "date_to=2026-04-23" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` ### Search by card (BIN + last digits) ```bash curl -G "https://api.rapidchargeback.com/api/v1/chargeback-alert/alerts" \ --data-urlencode "card_bin=424242" \ --data-urlencode "card_last4=4242" \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" ``` --- ## Response ### Success ```json { "data": [ { "id": "00000000-0000-0000-0000-000000000099", "company_id": "00000000-0000-0000-0000-0000000000c1", "merchant_id": "00000000-0000-0000-0000-000000000001", "merchant_enrollment_id": "00000000-0000-0000-0000-0000000000aa", "provider": "ethoca_alerts", "provider_alert_id": "ALERT-ETHOCA-12345", "amount": "150.00", "currency": "USD", "card_last4": "4242", "card_bin": "424242", "transaction_date": "2026-04-01T00:00:00.000Z", "descriptor": "LOJA EXEMPLO", "arn": null, "caid": null, "auth_code": "AUTH999", "alert_type": "fraud", "reason_code": "10.4", "issuer": "BANK XYZ", "status": "pending", "received_at": "2026-04-23T10:15:00.000Z", "expires_at": "2026-04-24T10:15:00.000Z", "created_at": "2026-04-23T10:15:00.000Z", "updated_at": "2026-04-23T10:15:00.000Z" } ], "meta": { "page": 1, "per_page": 20, "total": 42, "total_pages": 3 } } ``` ### No results ```json { "data": [], "meta": { "page": 1, "per_page": 20, "total": 0, "total_pages": 0 } } ``` ### Invalid validation (e.g. non-numeric `card_bin`) ```json { "error": { "code": "VALIDATION_ERROR", "message": "card_bin must be 6-8 digits" } } ``` --- ## Customers from the previous system Those who already integrated with the previous system can still look up an alert through the old endpoint, kept as an alias: ``` POST https://api.rapidchargeback.com/chargeback-alert/get ``` Note that this alias does **not** have the `/api/v1` prefix. It accepts Basic Auth or the `clientid`/`clientkey` headers. ### Request body Send **one** of the two: | Field | Type | Description | |---|---|---| | `alert_id` | string | **Alert ID at the provider**, the same one you receive in the `legacy` webhook format. Rapid's UUID is also accepted | | `authorization_code` | string | Transaction authorization code | If both are sent, `alert_id` takes priority. When more than one alert matches (for example, two alerts with the same authorization code), the most recent one is returned. ### Example ```bash curl -X POST https://api.rapidchargeback.com/chargeback-alert/get \ -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ -H "Content-Type: application/json" \ -d '{ "alert_id": "ETH-99887766" }' ``` ### Response `200` with `success` and `data`, where `data` is the **same body as the webhook in `legacy` format** (see [Legacy format](https://doc.rapidchargeback.com/en/canais/webhook/payload/#legacy-format)): ```json { "success": true, "data": { "alert_id": "ETH-99887766", "merchant": "Minha Loja", "provider": "ethoca", "descriptor": "LOJA EXEMPLO", "transaction_date": "2026-04-01T00:00:00.000Z", "currency": "USD", "amount": 150, "card_number": "424242******4242", "created_at": "2026-04-14T12:00:00.000Z" } } ``` The response comes with `Cache-Control: no-store`, as in the previous system. ### Errors of this alias To keep parity with the previous system, the errors of this endpoint use the **old** envelope (`{"error": "text"}`), not the new envelope with `code`: | Situation | Response | |---|---| | Neither `alert_id` nor `authorization_code` | `400` `{"error": "Informe alert_id ou authorization_code."}` | | None of your alerts matches | `404` `{"error": "Alerta não encontrado."}` | The messages are in Portuguese, exactly as the previous system returned them. In new integrations, prefer [`GET /api/v1/chargeback-alert/alerts/:id`](#endpoint), which uses the API's standard error envelope. --- ## Notes - **`provider`** comes as a slug string (`"ethoca_alerts"` or `"verifi_rdr"`), the same as in the outbound webhook payload (`v2` format). - The response fields are exactly those in the example above, plus `installment_number` and `installment_count` (installments, the same as in the webhook) and `provider_id`, an internal identifier kept for compatibility: use `provider`. - Date-only values (`transaction_date`) arrive as `"2026-04-01T00:00:00.000Z"`. - **Sorting:** always by `received_at` descending (most recent first). There is no custom sort parameter. - **Auth, rate limit, error codes:** see [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/) and [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/). --- # Authentication > Technical reference for every Rapid authentication mechanism: Basic Auth for your calls and the webhook signature for Rapid's calls. Página: https://doc.rapidchargeback.com/en/referencia/autenticacao/ This page is the technical reference for every Rapid authentication mechanism. ## Your application calling Rapid Use **Basic Auth** with `client_id` as the username and `client_secret` as the password. ``` Authorization: Basic base64(client_id:client_secret) ``` Endpoints that use this method: - [Transactions API](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/): create, get, update and delete transactions - [Merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/): list the company's merchants - [List alerts](https://doc.rapidchargeback.com/en/callback/consultar-alertas/) and [status callback](https://doc.rapidchargeback.com/en/callback/atualizar-status/) - [Evidence API](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/) and server-side capture (Dispute product) ### Credentials | Credential | Description | |---|---| | `client_id` | Your company's ID (generated in the dashboard) | | `client_secret` | Secret key (generated in the dashboard, treat it as a password) | ### Header example ``` Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ= ``` Where the base64 value decodes to `client_id:client_secret`. ### Security - Always use HTTPS - Do not include `client_secret` in frontend code, public repositories or logs - Rotate the `client_secret` if you suspect it was exposed; new credentials are generated in the dashboard --- ## Rapid calling your application (webhook) In the `v2` format Rapid signs each webhook with **HMAC-SHA256** and sends the signature and the timestamp in the headers: ``` X-Webhook-Signature: sha256= X-Webhook-Timestamp: 1790000000 ``` The signed content is **not just the body**: it is `${X-Webhook-Timestamp}.${raw_body}`, the timestamp, a dot and the body exactly as it arrived. Computing the HMAC over the body alone gives a different value and every request is rejected. The timestamp is part of the calculation to prevent a captured POST from being replayed later (reject those outside a 5-minute window). The signing key (`secret`) is **generated by Rapid** when you register the webhook and appears **only once** in the dashboard, under **Settings › Webhook**. You do not choose it and there is no way to look it up later: if you lose it, generate another under **Settings › API** (the previous one stops working immediately). The validation step by step, with examples in Node.js, Python and PHP, is in [Webhook authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). ### `legacy` format Accounts migrated from the previous system receive webhooks in the `legacy` format, which is **not signed**. In it, authentication is done by the headers the old system already sent: ``` clientid: clientkey: ``` These two headers exist **only in `legacy`**. They are not sent in `v2`: `clientkey` is your API access credential, and sending it with every delivery would expose it on your endpoint and in your logs for no reason, since in `v2` the signature does the authentication. To find out which format your account uses, see [Payload](https://doc.rapidchargeback.com/en/canais/webhook/payload/#legacy-format). --- # Response codes > Reference of the HTTP codes used across the whole Rapid API. Página: https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/ Reference of the HTTP codes used across the whole Rapid API. ## Success codes | Code | Meaning | |---|---| | 200 | Success with body | | 201 | Resource created | | 204 | Success without body (e.g. `DELETE`) | ## Client error codes | Code | Meaning | |---|---| | 400 | Body is not valid JSON (`INVALID_JSON`) | | 401 | Missing or invalid credentials | | 403 | Company blocked | | 404 | Resource not found | | 409 | Conflict (duplicate, immutable resource) | | 413 | Body above the maximum size (`PAYLOAD_TOO_LARGE`) | | 422 | Validation error | | 429 | Too many requests (rate limit) | ## Server error codes | Code | Meaning | |---|---| | 500 | Internal error (`INTERNAL_ERROR`), retry recommended | --- ## Error body format Every error response has this structure: ```json { "error": { "code": "SEMANTIC_CODE", "message": "Human-readable description of the error" } } ``` The `message` is in English and is meant for whoever reads the log: its wording can change. To decide what to do in your code, use the `code`. The endpoints kept from the previous system respond as they did before. ## Most common semantic codes | Code | HTTP | Use | |---|---|---| | `INVALID_JSON` | 400 | The body is not valid JSON | | `UNAUTHORIZED` | 401 | Missing or invalid credentials | | `COMPANY_BLOCKED` | 403 | Company blocked | | `VALIDATION_ERROR` | 422 | One or more invalid fields | | `NOT_FOUND` | 404 | The address does not exist in the API (typo in the path, extra trailing slash) | | `TRANSACTION_NOT_FOUND` | 404 | Transaction does not exist | | `TRANSACTION_DUPLICATE` | 409 | `external_source` + `external_id` already registered | | `TRANSACTION_IMMUTABLE` | 409 | Transaction already used, cannot be modified | | `MERCHANT_NOT_FOUND` | 404 | Merchant does not exist | | `ALERT_NOT_FOUND` | 404 | Alert does not exist | | `ALERT_INVALID_STATUS` | 422 | Invalid status, or transition not allowed (deadline, expiration or status already final) | | `PAYLOAD_TOO_LARGE` | 413 | Body above the limit: 1 MB per request, 5 MB in [batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/) | | `RATE_LIMIT_EXCEEDED` | 429 | Request limit exceeded (see below) | | `INTERNAL_ERROR` | 500 | Unexpected server error | ## Rate limit All routes share a limit of **100 requests per minute per IP**, including those that answer `401` for invalid credentials. The exception is [sending an event from the browser](https://doc.rapidchargeback.com/en/canais/capture/enviar-evento-navegador/), which has its own cap of 120 per minute and does not count toward the 100. When you go over it, the API returns: ``` HTTP/1.1 429 Too Many Requests Retry-After: 15 X-RateLimit-Limit: 100 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 15 ``` ```json { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded, retry in 15 seconds" } } ``` **Use `Retry-After`.** It says, in seconds, how long until the window resets, and the message repeats the same number. Waiting exactly that long is better than guessing: blind exponential backoff can retry too early (and get another `429`) or wait longer than needed. The `X-RateLimit-*` headers come in **every** response, not only in the `429`, so you can get ahead of it: when `X-RateLimit-Remaining` gets close to zero, hold the sending instead of waiting for the error. For volume, prefer [batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/), which sends up to 1000 transactions in one request and uses one unit of the limit. ## Retrying 5xx errors `5xx` errors are usually transient. Retry with increasing intervals between attempts. Never retry `4xx`: they mean an error on your side and will fail again. --- # Common problems > Index by symptom of the most common integration problems, with the likely cause and the page that solves each one. Página: https://doc.rapidchargeback.com/en/referencia/problemas-comuns/ Found your symptom? The likely cause is in one line, and the detail is on the linked page. ## Credentials and access ### Every call returns `401` The `Authorization` header does not match: wrong credentials, credentials regenerated in the dashboard (the previous ones stop working immediately), or base64 built over something other than `client_id:client_secret`. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ### The API returns `403 COMPANY_BLOCKED` The company is blocked at Rapid. Credential-based calls stop; webhooks keep arriving. See [Blocked companies](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/#blocked-companies). ### I get `429` You went over the request limit. Wait the time in the `Retry-After` header and, for volume, use batch upload. See [Rate limit](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/#rate-limit). ### I get `404 NOT_FOUND` on an endpoint that exists The path differs: an extra trailing slash, missing `/api/v1` or a typo. See [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/). ## Webhook ### No webhook arrives First check that the webhook is registered and active under **Settings › Webhook**: without that there is no delivery and nothing in the log. Then check the reason for each attempt in the dashboard's delivery log, under **Activity**, **Webhooks** tab: it starts with `[https]`, `[destino]`, `[redirecionamento]` or `[assinatura]` when the failure was not a response from your server. See [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/#delivery-logs). ### The signature never matches Almost always the body was parsed as JSON before validating: the signature covers the raw body, byte for byte. Other causes: a key that was already rotated, and a timestamp outside the window. Test it with the [signature checker](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/#signature-checker) and compare with the examples in [Webhook authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). ### The log shows a failure, but my server received it Your server answered outside `2xx` or took longer than the time limit. See [What counts as success](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/#what-counts-as-success). ### The same alert arrived twice It is a retry or a redelivery, with the same `alert_id`. See [Idempotency](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/#idempotency). ## Alerts ### `422 ALERT_INVALID_STATUS` when responding to an alert The deadline passed or the alert already has a final status. The message says which case it is. See [Transition not allowed](https://doc.rapidchargeback.com/en/callback/atualizar-status/#transition-not-allowed). ### I do not know the alert `id`, only the provider's Search by `provider_alert_id`. See [Search by your own ID](https://doc.rapidchargeback.com/en/callback/consultar-alertas/#search-by-your-own-id-the-providers). ## Transactions ### `409 TRANSACTION_DUPLICATE` You already have a transaction with the same `external_source` and `external_id`. See [Error codes](https://doc.rapidchargeback.com/en/canais/transactions/codigos-de-erro/). ### `409 TRANSACTION_IMMUTABLE` The transaction was already used by a product and can no longer be changed or deleted. See [Immutability protection](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/#immutability-protection). ### `404 MERCHANT_NOT_FOUND` The `merchant_id` is not one of your company's merchants. See [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/). ### I lost the transaction `id` Find it by the identifier you sent. See [By your system's ID](https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/#by-your-systems-id). ### `413 PAYLOAD_TOO_LARGE` in batch upload The body went over the maximum size. Split it into smaller batches. See [Batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/). ### The response has `warnings` The transaction was created, but fields that improve protection are missing. See [Warnings](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/#warnings). ### A dispute arrived without the associated transaction The transaction does not have the charge identifier in the gateway. See [Note on `transaction_id`](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/#note-on-transaction_id). --- # Glossary > The payment and integration terms used in this documentation, from ARN to webhook, with what each one means for whoever integrates with Rapid. Página: https://doc.rapidchargeback.com/en/referencia/glossario/ The terms that appear in this documentation, in alphabetical order. When the term is an API field, the field name comes in parentheses. ## 3-DS Cardholder authentication in an online purchase, done by the issuer (SMS code, bank app, biometrics). An authenticated purchase weighs in the store's favor in a fraud dispute. ## Acquirer The company that processes the store's card payments and passes the money on to it. The chargeback is charged to the store on the acquirer's side. ## Alert Notice that a cardholder opened a dispute at the issuing bank, before it becomes a chargeback. See [Alert](https://doc.rapidchargeback.com/en/produtos/alerta/visao-geral/). ## Alert status (`status`) The state of the alert: `pending`, `notfound`, `account_suspended`, `other` or `expired`. See [Rules and deadlines](https://doc.rapidchargeback.com/en/produtos/alerta/regras-e-prazos/). ## ARN (`arn`) *Acquirer Reference Number*: the number the acquirer gives the transaction at clearing. It helps find the same purchase in the systems of everyone involved. ## Authorization code (`auth_code`) The code the issuer returns when it approves the purchase. ## BIN (`card_bin`) The first 6 to 8 digits of the card. They identify the issuing bank and the card type. ## CAID (`caid`) *Card Acceptor ID*: the store's identifier at the acquirer. Together with the acquirer's BIN (not the card's), it is how Visa alerts are matched to your company. ## Capture The recording of checkout signals (device, terms acceptance, access, delivery) that become proof in the defense. See [Capture](https://doc.rapidchargeback.com/en/canais/capture/visao-geral/). ## Card network (`network`) The card's network, such as Visa or Mastercard. It sets the dispute rules and deadlines. ## Cardholder The person who owns the card used in the purchase. ## Chargeback The forced reversal of a purchase: the cardholder disputes it at the issuing bank and the amount goes back to them, charged to the store. ## Credentials (`client_id`, `client_secret`) The pair that authenticates API calls with Basic Auth. Generated in the dashboard. See [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/). ## Descriptor (`descriptor`) The store name that shows on the card statement. It is how Mastercard alerts are matched to your company, and the reason for many disputes: whoever does not recognize the name disputes the charge. ## Dispute A cardholder's challenge of a purchase. Also the name of the Rapid product that defends a chargeback already opened. See [Dispute](https://doc.rapidchargeback.com/en/produtos/disputa/visao-geral/). ## ECI (`eci`) *Electronic Commerce Indicator*: the indicator that says whether and how the online purchase was authenticated (for example, with 3-DS). ## Evidence A proof recorded about a transaction (terms acceptance, access, delivery, communication) to support the defense. See [Evidence](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/). ## Idempotency Processing the same event twice with no duplicate effect. Needed because the webhook can arrive more than once. See [Idempotency](https://doc.rapidchargeback.com/en/canais/webhook/boas-praticas/#idempotency). ## Issuer The bank that issued the cardholder's card. It is where the dispute starts. ## MCC (`mcc`) *Merchant Category Code*: the four-digit code that classifies the store's line of business. ## Merchant A store, brand or seller of a customer company. A company can have several. See [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/). ## Prevention The Rapid product that answers the issuing bank with the order data so the dispute is never born. See [Prevention](https://doc.rapidchargeback.com/en/produtos/prevencao/visao-geral/). ## Publishable token The capture snippet token, which lives in the store's HTML. It only allows sending events, never reading data. See [Install the snippet](https://doc.rapidchargeback.com/en/canais/capture/instalar-o-snippet/). ## Signing key (`secret`) The key Rapid generates when you register the webhook and uses to sign each delivery. It is shown only once in the dashboard. See [Webhook authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). ## Transaction A sale sent to Rapid through the [Transactions API](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/). It is the raw material of Prevention and Dispute. ## Webhook The POST Rapid makes to the URL you registered, on each event (new alert, access revocation). See [Webhook](https://doc.rapidchargeback.com/en/canais/webhook/visao-geral/). ## Webhook signature The HMAC-SHA256 code Rapid sends in the `X-Webhook-Signature` header, computed with your signing key. It proves the delivery came from Rapid. See [Webhook authentication](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/). --- # Canonical examples > The fixed IDs, values and credentials of every example in the documentation, so that one page's example connects to another's. Página: https://doc.rapidchargeback.com/en/referencia/exemplos-canonicos/ Fixed values used in every example of the documentation. Use them as a reference when reading curl samples and payloads: the same IDs appear on different pages on purpose, so that a transaction example connects to an alert example. ## Default values ### IDs generated by Rapid They all follow the same pattern, `00000000-0000-0000-0000-` plus a suffix, to make it obvious they are examples. A real ID never has this format. | What it identifies | Field | Value | |---|---|---| | Transaction | `id`, `transaction_id` | `00000000-0000-0000-0000-000000000042` | | Merchant | `merchant_id` | `00000000-0000-0000-0000-000000000001` | | Alert | `alert_id` | `00000000-0000-0000-0000-000000000099` | | Dispute | `dispute_id` | `00000000-0000-0000-0000-0000000000d1` | | Evidence | `id` | `00000000-0000-0000-0000-0000000000e1` | | Your company | `company_id` | `00000000-0000-0000-0000-0000000000c1` | | Merchant enrollment | `merchant_enrollment_id` | `00000000-0000-0000-0000-0000000000aa` | Transaction `…042` is the same on every page: it is the one you create in [Create transaction](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/), then get, update and delete on the following pages, and it is the one the [evidence](https://doc.rapidchargeback.com/en/canais/evidence/registrar-evidencia/) points to. ### Transaction values | Field | Value | |---|---| | `external_id` (your transaction ID) | `TXN-2026-001` | | `amount` | `150.00` | | `currency` | `USD` | | `transaction_date` | `2026-04-01T15:30:00Z` | | `card_last4` | `4242` | | `card_bin` | `424242` | | `descriptor` | `LOJA EXEMPLO` | | `arn` | `74537119547024600128228` | | Customer webhook URL | `https://seu-sistema.com/webhooks/rapid` | | API base URL | `https://api.rapidchargeback.com/api/v1` | ## Basic Auth header Every example uses the same encoded header: ``` Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ= ``` The base64 value decodes to `client_id:client_secret`. In your code, replace it with your company's real credentials. ## Example names and addresses - Customer: `João Silva`, email `joao@exemplo.com` - Address: `Rua das Flores, 123, São Paulo, SP, 01001000, BRA` These values have no special meaning; they are just placeholders that stay consistent across pages. They are in Portuguese because they are the same in every language of this documentation. --- # Developer tools > The documentation in your AI assistant (MCP), as Markdown and llms.txt, and the Postman collection with every request ready to use. Página: https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/ Shortcuts to integrate faster: the documentation inside your AI assistant and the requests ready to test. ## MCP The MCP server lets your AI assistant (Claude, Cursor, VS Code, ChatGPT and others) **search and read this documentation** while you code. Instead of answering from memory, it looks up the right page and uses the real field names, accepted values and error codes. ``` https://doc.rapidchargeback.com/mcp/ ``` It only reads the public documentation: it asks for no credentials, does not access your Rapid account and does not call the API. The documentation is available in Portuguese, English and Spanish; ask the assistant to use English (the tools take a `language` parameter). ### Claude Code ```bash claude mcp add --transport http rapid-docs https://doc.rapidchargeback.com/mcp/ ``` ### Cursor In `~/.cursor/mcp.json` (or `.cursor/mcp.json` in the project): ```json { "mcpServers": { "rapid-docs": { "url": "https://doc.rapidchargeback.com/mcp/" } } } ``` ### VS Code In `.vscode/mcp.json` in the project: ```json { "servers": { "rapid-docs": { "type": "http", "url": "https://doc.rapidchargeback.com/mcp/" } } } ``` ### Claude (app and web) Under **Settings > Connectors > Add custom connector**, enter the name `Rapid` and the URL above. ### Other clients Any MCP client with HTTP transport works with the same URL. ### What the assistant can do | Tool | What it does | |---|---| | `search_docs` | Searches by topic, field name, error code, header or endpoint path. Corrects typos | | `get_page` | Reads a whole page in Markdown | | `list_pages` | Lists every page, in reading order | Example request: *"Using the Rapid documentation, write the alert webhook handler in Node.js, validating the signature."* ## Copy a page Every page has the **Copy page** button, next to the title. It copies the page as Markdown, the format you can paste into an AI assistant without losing tables or code blocks. The menu next to the button opens the same page directly in ChatGPT or Claude. Each page is also available as Markdown at its own address, replacing the trailing slash with `.md`: ``` https://doc.rapidchargeback.com/en/canais/webhook/payload.md ``` ## llms.txt For tools that read the documentation all at once: | File | Content | |---|---| | [`/en/llms.txt`](https://doc.rapidchargeback.com/en/llms.txt) | Index of every page, with a description and a link to the Markdown | | [`/en/llms-full.txt`](https://doc.rapidchargeback.com/en/llms-full.txt) | The whole documentation in a single file | ## Postman collection Every example request in this documentation, organized by the menu sections. 1. Download the [collection](https://doc.rapidchargeback.com/en/rapid.postman_collection.json). 2. In Postman, click **Import** and choose the file. 3. In the collection variables, fill in `client_id` and `client_secret` with the credentials from the dashboard (see [Authentication](https://doc.rapidchargeback.com/en/referencia/autenticacao/)). Basic authentication is already set up in the collection. The IDs in the examples are fictitious (see [Canonical examples](https://doc.rapidchargeback.com/en/referencia/exemplos-canonicos/)): replace them with your account's before sending. The collection is generated from the examples on the pages, so it follows the documentation. --- # API changelog > What changed in the Rapid integration API, by date: new endpoints, behavior changes and what still works from the previous system. Página: https://doc.rapidchargeback.com/en/referencia/novidades-da-api/ Changes to the integration API, newest first. Dashboard-only changes are not listed here. ## October 2026: new API The current version of the API, replacing the one from the previous system. If you already integrated with Rapid, this is the list of what changes. ### New - **Transactions API** at `/api/v1/transactions`: [create](https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/), [send in batch](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/) (up to 1000 per call), [retrieve](https://doc.rapidchargeback.com/en/canais/transactions/consultar-transacao/) by Rapid's ID or by your system's ID, [update](https://doc.rapidchargeback.com/en/canais/transactions/atualizar-transacao/) and [delete](https://doc.rapidchargeback.com/en/canais/transactions/deletar-transacao/). - **[List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/)**, to find the `merchant_id` through the API. - **[List alerts](https://doc.rapidchargeback.com/en/callback/consultar-alertas/)** with filters (status, dates, card, provider ID) and pagination. - **[Update status](https://doc.rapidchargeback.com/en/callback/atualizar-status/)** of the alert at `PATCH /api/v1/chargeback-alert/alerts/:id/status`. - **`v2` webhook format**: delivery [signed](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/) with HMAC-SHA256 and a timestamp, event identified in the body and in a header, and a single channel for every product. - **[Evidence](https://doc.rapidchargeback.com/en/canais/evidence/visao-geral/) and [Capture](https://doc.rapidchargeback.com/en/canais/capture/visao-geral/)**, which feed the Dispute defense. - **A single error format** across the whole API (see [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/)). - **[Developer tools](https://doc.rapidchargeback.com/en/referencia/ferramentas-para-devs/)**: the documentation in your AI assistant (MCP), in Markdown and in `llms.txt`, and the Postman collection. ### Changed - **Webhook only over HTTPS and to a public internet address.** A redirect that turns the POST into a GET (`301`, `302`, `303`) is not followed. See [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/). - **Request limit** per IP, also counting `401` responses, with `Retry-After` on the `429`. See [Response codes](https://doc.rapidchargeback.com/en/referencia/codigos-de-resposta/#rate-limit). - **Webhook retries** at defined intervals. See [Retries and logs](https://doc.rapidchargeback.com/en/canais/webhook/retries-e-logs/). ### Still works, from the previous system Kept for those who already integrated, with no change on your side. For a new integration, use the endpoints in the right column. | From the previous system | Use in a new integration | |---|---| | `POST /chargeback-alert/update/status` | [`PATCH /api/v1/chargeback-alert/alerts/:id/status`](https://doc.rapidchargeback.com/en/callback/atualizar-status/) | | `POST /chargeback-alert/get` | [`GET /api/v1/chargeback-alert/alerts`](https://doc.rapidchargeback.com/en/callback/consultar-alertas/) | | Webhook in the [`legacy` format](https://doc.rapidchargeback.com/en/canais/webhook/payload/#legacy-format), with `clientid`/`clientkey` | Webhook in the `v2` format, with a [signature](https://doc.rapidchargeback.com/en/canais/webhook/autenticacao/) | These old endpoints will be removed once there are no more calls to them. ### No equivalent The **previous system's Transactions API** (`/transactions/create`, `/transactions/update` and `/transactions/delete`, without `/api/v1`) does not exist in the new API. The path and the fields changed: the integration needs to move to the new [Transactions API](https://doc.rapidchargeback.com/en/canais/transactions/visao-geral/).