A refund that can only go back where the money came from.
The customer complains in the support chat. The agent finds the payment's endToEndId and asks CodeSpar to return the Pix: the money goes back to the original payer, and the refund can never be more than what is left to return of that payment. Each refund carries an idempotency key, so a retry is the same refund. The refund is not checked against a mandate or a cap; the original payment is its bound. Issuing a credit note and looking the order up in your ERP are your integration.
The refund, from the chat.
An example with demo data: the customer asks for a refund in the support chat; the agent finds the Pix payment and asks CodeSpar to return it to the payer. The conversation is in Portuguese, as the customer wrote it.
- The refund goes back to the original payer, on the same rail
- Never more than what is left to return of the payment
- Not checked against a mandate or a cap: the payment is the bound
The conversation is the easy part.
Refunds are where support meets money: the ticket waits for someone with bank access, the manual transfer does not match the original payment, and nobody can say later what was returned.
Escalate every refund to whoever holds the bank login
The agent returns the Pix through the API, with its own key
A manual transfer that does not match the original payment
The refund names the payment's endToEndId and goes back to its payer
Refund twice after a timeout
The idempotency key makes a retry the same refund
Refund more than was paid
The refund cannot exceed what is left to return of the payment
A refund bounded by the payment.
The agent calls POST /v1/consumers/{consumerId}/pix/devolutions with the payment's end_to_end_id, the amount in centavos and an idempotency key; a GET on the devolution reads its status. The refund goes back to the original payer and is bounded by what is left to return of that credit. It does not consume a mandate. Looking the order up, issuing a credit note and telling the customer are your integration.
The customer asks for a refund
The payment's endToEndId
Pix back to the payer
Read by id
The customer, in the chat
The ticket enters from your support channel. The agent resolves the order and its Pix payment in your systems, then calls the devolution route with the endToEndId. CodeSpar checks the payment against the consumer's own statement, refuses a second reversal of the same money, reverses the wallet entry and dispatches the return on Celcoin; its status is read on the same route. A mandate is not consumed: the return is pinned to the original payer and bounded by the original credit.
The code, and what it assumes.
// Prerequisites: CODESPAR_API_KEY set to a test key (csk_test_…), a consumer // (user_0000) whose wallet received the Pix, and that payment's endToEndId. import { CodeSpar } from "@codespar/sdk"; const cs = new CodeSpar(); // return R$ 186.00 of the payment to its payer; never more than what is left to return const res = await cs.api.response("post", "/v1/consumers/{consumerId}/pix/devolutions", { path: { consumerId: "user_0000" }, body: { end_to_end_id: "E00000000202610081200abcdef123456", amount_minor: 18_600, idempotency_key: "refund-8421" }, }); console.log(res.status, res.ok ? res.data : null);
Example with the published SDK (@codespar/sdk 0.16.11). Its prerequisites are in the comments. Compiling does not prove the integration: run it in test mode first.
POST /v1/consumers/{consumerId}/pix/devolutionsReturns a received Pix to its payer by endToEndId, bounded by the original credit, with a required idempotency key.
GET /v1/consumers/{consumerId}/pix/devolutions/{devolutionId}Reads the refund's status.
Start with a test key.
Test keys are free. Open the sandbox, connect the providers your project uses and see which tools it can call before you build.