polling steps are the part nobody guesses. One at a time.
Path A: you have the invoice (checkout())
You have had invoiceId from the start, so this is the short path:
invoiceId → your user pair before showing the screen. It is how
you will know whose purchase it was when the payment comes back.
Path B: you sent a payment link
The link is the recommendation for anyone who does not want to build a screen. If you want to control the experience and still use a link — or to test the flow over HTTP — it is three steps, and the invoice only exists at the third.- The invoice does not exist before step 3. The session has no
invoiceId— it appears in the charge response. Do not look for it earlier. - The charge is on the session, not on the invoice.
/sessions/{id}/charge, not/invoices/{id}/charge. The second one exists and serves path A. - No
email→400 "E-mail is required."; notaxId→400 "A valid CPF or CNPJ is required."Both in Portuguese, both before any charge happens.
These routes do not require an
Idempotency-Key. The rule “every non-GET method requires the key” applies to the authenticated
API. The public /pay/* routes — the ones the buyer’s browser calls — accept
requests without it.Finding out they paid
timeoutMs. The default is 600000 — ten minutes — so on an
unpaid invoice, which is the normal case, your handler hangs instead of
answering.
In production the payment.confirmed webhook is the right path; in sandbox
registering one answers 503, so it is polling — details in
webhooks.
Delivering — and the second poll
Here is the mistake you can make with the whole doc open in front of you: the grant does not exist the instant the invoice turnspaid. Delivery runs after
the payment confirms, so the first read returns [].
200, never a 404 — deliberately, precisely so this is
safe to poll in a loop. The rest of the deliverable is in
delivering the product.
Testing all of this end to end
In sandbox you close the loop yourself, with no provider key:pixPayload — in production the field
does not come and the payload is EMV. That is what separates a test button from
a production bug. It is spelled out in
testing in the sandbox.
The whole page, together
download coming back undefined is not an error: it is a product with no
deliverable, or delivery that has not run yet. In both cases, showing “your
access arrives by email in a moment” beats an empty screen — and the email does
go out, as long as the address is real.