Skip to main content
OpenDelivery

Zupy is an OpenDelivery 2.0-compliant participant

Zupy is OpenDelivery 2.0 compliant for the Customer (Loyalty) capability. If your platform already speaks OpenDelivery, you can integrate with Zupy loyalty using the standard — discover us via our manifest, authenticate with client_credentials, and receive signed events. No Zupy-proprietary contract required.

What OpenDelivery is

OpenDelivery is an open Brazilian standard that defines how ordering platforms, delivery logistics, and merchant software interoperate. Zupy participates as a Customer capability provider (loyalty programs, accounts, transactions, rewards, coupons, and redemptions), so any OpenDelivery-aware ordering application can read a customer’s loyalty state and issue redemptions through a documented, non-proprietary interface.

1. Discovery — find us via the manifest

Zupy publishes a public Discovery manifest at the well-known path. It advertises our protocol version, authentication models, and the exact operations we support — no need to coordinate out-of-band.
Discovery request
Manifest (live)
appId is Zupy’s single, stable participant identity — the same value appears in every webhook we send (X-App-Id). It is not per-partner.

2. Authentication — two models, both standard

OpenDelivery allows a provider to declare more than one clientIdGeneration model. Zupy declares both, so you pick the one that fits your integration:

by_app (recommended)

One credential for your application, valid across all merchants linked to it. Best for platforms that integrate many Zupy merchants. Each merchant is activated by the merchant themselves via a toggle in their Zupy dashboard. Provisioned by the Zupy partnerships team.

by_merchant (self-serve)

One credential per merchant, provisioned by the merchant from their own dashboard (/integrations). Best when a single merchant wants to connect an app Zupy hasn’t formally partnered with yet. No partnership required.
Both models use the client_credentials OAuth2 grant to obtain a bearer token, and both are exposed identically at https://api.zupy.com/v2.

Selecting a merchant with by_app

A by_app token can reach multiple merchants, so each request must name which merchant it targets using the X-App-MerchantId header. Zupy only serves data for merchants whose toggle links your application — anything else is denied.
X-App-MerchantId
string
The merchant identifier as your application knows it (the OpenDelivery merchantId). Zupy maps it to the merchant internally. Required on every by_app read.

3. Merchant identity — two-sided by design

OpenDelivery models a merchant with two identifiers, and Zupy stores both: Call GET /v2/merchants with your token to list every merchant your credential can reach, each returned with both identifiers.

4. Supported operations

All eight Customer/Loyalty operations are live at https://api.zupy.com/v2. List operations are paginated ({ items, page, pageSize, total }).
Redemption is asynchronous: POST /loyalty/redemptions returns 202 with a correlationId. A 409 means the account can’t afford the reward. Redemptions are single-unit (quantity must be 1).

5. Webhooks — signed OpenDelivery events

When loyalty state changes, Zupy sends a signed event to your registered webhook URL. Events use the OpenDelivery envelope (id, eventType, occurredAt, accountId, customerId, programId, payload). Every webhook carries these headers:
X-App-Id
string
Zupy’s participant appId — identical across all our webhooks.
X-App-MerchantId
string
The merchant this event belongs to (the merchantId you assigned).
X-App-Signature
string
Lowercase hex HMAC-SHA256 of the raw request body, signed with your webhook secret — no sha256= prefix. Verify it before trusting the payload.
Always verify X-App-Signature against the raw body bytes (not a re-serialized JSON) using your webhook secret. Reject any request whose signature does not match.

6. Inbound — accrue points from orders

If your platform owns the orders, you can have Zupy award loyalty points automatically when a sale happens — and reverse them if that sale is cancelled. Zupy is the loyalty host: you send two signals, we do the rest (we never track your order lifecycle — when to send each signal is entirely your call). Send an OpenDelivery CustomerEventEnvelope to:
Accrue / reverse
Both are authenticated with the same OAuth2 credential you use for reads (by_app or by_merchant) — no separate scheme. A minimal envelope:
order.created
customerIdentifier
object
{ type, value }type is phone or email (a document/CPF-only customer is skipped, no lookup). The customer is enrolled automatically if new.
payload.totalAmount
number
Order total in cents (OpenDelivery convention). Zupy converts to the base currency before applying the points rate.
Idempotent by orderId. Re-sending the same order.created never double-credits; re-sending order.cancelled never double-reverses. A cancel for an order that was never credited is a no-op.
Accrual is opt-in per merchant. A merchant must have an OpenDelivery accrual rate configured; until then, order.created is accepted (204) but awards nothing. When points are awarded, Zupy emits the loyalty.points.earned webhook back to you (§5).
A response of 204 means accepted. Unknown OpenDelivery event types are rejected (422); other known-but-non-loyalty events (e.g. order.delivered) are acknowledged (202) and ignored.

Reference

OpenDelivery spec

The normative OpenDelivery 2.0 specification.

Partner Onboarding

What you receive and how to go live.

Outbound Webhooks

Webhook delivery, retries, and signature verification in depth.

Get a credential

Contact the Zupy partnerships team for a by_app credential.