Ad-hoc
Integrate Nelo Checkout directly against our REST API, without a platform plugin.
Use this guide when you are building the integration yourself — your own storefront, your own
backend, or a platform we do not have a plugin for. If you are on VTEX or Shopify, use the plugin
instead; it performs all of the steps below for you.
Nelo works like any other payment method: you authorize an amount while the shopper is in
checkout, then capture it when you are ready to charge — typically on shipment. Everything in
between happens on Nelo's hosted checkout, not in your code.
This page explains how the pieces fit together. Every endpoint, field and response is in the Nelo Checkout API reference.
Before you start
You need a Nelo account. We create it and send you two access tokens, both sent as
Authorization: Bearer <token>:
| Token | Use it for |
|---|---|
| Secret | Everything below. Server-side only. |
| Publishable | Promotional messages only. Safe in a browser. |
The secret token is the one that moves money. Keep it on your server and never ship it to a browser
or a mobile app. The publishable token is deliberately limited — it is accepted only on the
promotional-message endpoints, so exposing it cannot create, capture, void or refund anything.
Every endpoint in this guide lives under /v1 — POST /v1/checkout/order, and so on. We give you
the base URL along with your tokens.
A sandbox environment is being prepared. Until it is available, talk to us before testing.
The flow
- Shopper picks Nelo at your checkout.
- Your backend creates an order and gets back an
orderUuidand a
redirectUrl. - You send the shopper there. They apply, pick a payment plan and confirm on Nelo.
- Nelo returns them to your
redirectConfirmUrl, or toredirectCancelUrlif they back out. - Your backend confirms the outcome and, when you are ready to charge, captures.
Step 5 is not optional. Treat the redirect as a hint that something happened, never as proof that it
succeeded — a shopper can close the tab, lose signal, or hit the back button. The API is the source
of truth.
Amounts are always in minor units (cents).
249900is $2,499.00 MXN. Sending2499creates a
$24.99 order. This is the most common integration mistake.
The two identifiers
You send two of your own ids when you create an order, and they do different jobs. Getting this
right is most of a correct integration.
reference is your order number. It is what you and our support team use to find the order, and
what identifies it to us when you get in touch. Up to 255 characters. We do not require it to be
unique, so searching by it can return more than one order.
platformPaymentId is your id for this payment attempt, and it is required. It is what makes
creating an order safe to retry: re-send a create with the same platformPaymentId and the same
reference, and you get back the order you already created, with the same checkout URL, instead of
a second order and a second URL.
So if a create times out, retry it with the whole payload unchanged. Changing either value gives you
a new order.
It is also how you recover when a create times out and you never see the response — and so never see
the orderUuid it returned. Looking the order up by platformPaymentId tells you whether an order
already exists before you try again.
Use a fresh platformPaymentId for every new order, and reuse one only when retrying the create
for that same order.
Finding an order
Creating an order returns its orderUuid, and that is what capture, void
and refund take. Store it against your own order and you will not need to look it up.
When you do need to — a create that timed out, or reconciling after the fact — look it up by the
identifiers you chose, with Find orders. It returns the orderUuid and the
order's status.
You can query by platformPaymentId, by reference, or both. Searching by reference alone can
return several orders, if you have charged that order more than once.
Order status
An order reports one of five statuses, plus null before the shopper starts:
| Status | Meaning | What to do |
|---|---|---|
null | Checkout created; the shopper has not started an application. | Wait. |
CREATED | Application started, not confirmed. Not an authorization. | Wait. Do not fulfil. |
AUTHORIZED | Approved. Funds are reserved. | Capture when you are ready to charge. |
ACTIVE | Captured. The shopper is repaying Nelo. | Fulfil. |
PAID | Fully repaid. | Nothing. |
CANCELED | Voided, declined, or abandoned. | Release the goods. |
PAID and CANCELED are final. The others can still change.
Authorizations expire after 3 days. An order the shopper never confirms is cancelled
automatically. Capture within that window, or you will need a new order.
What you get paid
Get order returns the money view of a single order: the amount, Nelo's fee and the tax on it, and
netAmount — what actually reaches you — along with whether the order is still refundable and the
refunds made against it so far.
It does not carry the order's status. Use Find orders for that.
Redirects
Nelo returns the shopper to the URLs you supplied, exactly as you supplied them — we do not
append an order id or any other query parameter. If you need to know which order came back, encode
it yourself:
https://store.example.com/nelo/return?order=ORDER-1042
Both URLs must be absolute, and redirectCancelUrl is required in the default redirect mode. The
other modes do not use it: in-store mode texts the checkout link to the shopper instead of returning
them to a browser.
Capturing, voiding and refunding
Capture charges an authorized order. Void releases one you are not going to charge. Both are safe to repeat: an
already-captured order succeeds without charging again.
If you fulfil immediately, set shouldAutoCapture when you create the order and Nelo captures as
soon as the shopper confirms — you never call capture at all.
Refund returns money on a captured order, in full or in part. Partial
refunds may be repeated while the payment is still refundable.
Webhooks
Nelo POSTs to your callback URL when an order reaches an outcome, so you do not have to poll.
Give us one URL when we set your account up and we use it for every order — you can then leave
merchantCallbackUrl out of your create calls entirely. Send it on a create only when that order
needs to go somewhere else; it overrides the account-level URL for that order alone. An endpoint for
changing the account-level URL yourself is coming.
Either way, what arrives looks like this:
POST <your merchantCallbackUrl>
Content-Type: application/json
X-Nelo-Event: order.authorized
X-Nelo-Signature: <base64 signature>
{
"event": "order.authorized",
"orderUuid": "3f9b1e42-...",
"reference": "ORDER-1042",
"platformPaymentId": "pay_8f31c0",
"occurredAt": "2026-09-04T22:49:25.955Z"
}
Two events are sent: order.authorized when the shopper is approved and the order is ready to
capture, and order.canceled when it is voided, declined or abandoned. The event is the outcome,
which is why the payload carries no status or amount — there is nothing in it that can go stale
between us sending it and you reading it.
Delivery is at least once, and events can arrive out of order, so treat one as a nudge rather than
as truth: read the order back with Find orders and act on what the API says.
Deduplicate on orderUuid and event — an event you have already handled should be a no-op.
Respond 2xx quickly. Do the work afterwards, not before you answer.
Verifying a callback
Every request carries X-Nelo-Signature: a base64 SHA256withRSA signature over the request body,
made with Nelo's private key. Verify it with our public key before acting, and reject anything that
does not match — your callback URL is reachable by anyone who learns it.
Verify against the exact bytes you received, not against a re-serialized copy of the parsed JSON.
Re-encoding can reorder keys or change spacing, and the signature will no longer match.
Retrying safely
Any call can time out with the work already done. Two mechanisms make retries safe, and they cover
different things.
The Idempotency-Key header works on creating an order, capture, void and refund. Send a unique
value per logical request; retrying with the same key and the same body replays the original
response instead of repeating the work. Reusing a key with a different body is rejected. Keys are
remembered for 24 hours.
Idempotency-Key: ORDER-1042-capture-1
The body fields — platformPaymentId when creating an order, and idempotencyKey on a
refund — are stored with the order, so they protect you beyond that 24-hour window and even if you
forget the header. Use both.
A refund sent without an idempotencyKey is the one call that is genuinely unsafe to retry: every
attempt refunds again.
Errors
Errors return the HTTP status and a JSON body:
{ "message": "Order platformPaymentId is required...", "status": 400 }| Status | Cause |
|---|---|
400 | A missing or invalid field. The message says which. |
401 | Missing, malformed or revoked token. |
403 | A valid token without the rights for that endpoint — usually a publishable token where the secret one is needed. |
404 | Unknown order, or one belonging to another merchant. |
503 | Nelo Checkout is unavailable for your account. Do not retry in a loop — fail over to another payment method and tell us. |
Promotional messages
Show shoppers what Nelo costs before they reach checkout. Those endpoints accept the publishable
token, so you can call them from the browser — see Promotional messages.
Updated 13 minutes ago