Orders
Every order is a complete record of a transaction, from line items and customer data to payment state and fulfillment information. The APIs below give you precise control over the full order lifecycle, whether you're charging immediately or deferring payment until later.
The order object
An order object holds everything—the cart, payment intent, customer profile, and current state. You can create orders with inline customer data for one-time checkouts or reference saved customers and payment methods for frictionless repeat purchases.
Properties
line_item_groupobjectCart contents and totals.Click or tap to expandpaymentobjectPayment intent tied to this order.Click or tap to expand- Name
latest_attempt- Type
- object
- Description
- Details about the most recent charge attempt. Omitted before the first attempt.
View latest_attempt attributesClick or tap to expand
View next_action attributesClick or tap to expand
View confirm_payment attributesClick or tap to expand
View request attributesClick or tap to expand
View payment_method attributesClick or tap to expand
- Name
payout_configuration- Type
- object
- Description
- Payout routing instructions for this payment. Present only when order-level
payout_settingswas specified during order creation.
View payout_configuration attributesClick or tap to expand
Create an order
Create a new order for a first-time or returning customer, and decide whether to charge it now or later. Product line items can be fully inline, or they can reuse products and prices you already keep in your catalog.
Clients should treat the published route in this reference as authoritative and should not construct alternative order-creation paths.
Rules
- Exactly one of
customer_idorcustomer_datais required. - Set
execute_paymenttotrueonly when a payment method is attached (payment_method_idorpayment_method_data). Omit it or passfalseto create the order first and charge it later. - Every line item in the request must use the same currency.
- Product line items support three valid shapes: inline product data,
product_idplus explicitprice, orproduct_idplusprice_id. - When you send
product_id, Commerce snapshots the current catalog product onto the order. Do not combineproduct_idwith inline product fields such asname,type,about,reference,tax_code, orcustom_data. - When you send
product_id, you must also send eitherpriceorprice_id. Do not send both. Commerce does not infer the amount from the product's default unit price. - When provided,
receipt_numberis trimmed, must satisfy the same configured length limits asnumber, and does not need to be unique. - To make retries safe, send a stable
request_meta.idempotency_keyor theIdempotency-Keyheader. Same-key retries with the same operation payload replay the original success; same-key retries with a different operation payload returnidempotency_key_conflict.
Required attributes
line_itemsarrayCart contents. Provide at least one line item per order.Click or tap to expand- Name
product- Type
- object
- Description
- Required when
typeisproduct. Use one of three shapes: inline product data,product_idwith explicitprice, orproduct_idwithprice_id.
View product attributesClick or tap to expand
View fee attributesClick or tap to expand
View shipping attributesClick or tap to expand
Product line item shapes
Use the shape that matches how you manage your catalog and pricing:
Inline product data
{
"type": "product",
"product": {
"name": "Utility Sneakers",
"type": "physical",
"quantity": 1,
"price": { "currency": "ghs", "value": 20000 }
}
}
Catalog product with explicit price
{
"type": "product",
"product": {
"product_id": "prod_abc123xyz",
"quantity": 2,
"price": { "currency": "usd", "value": 4100 }
}
}
Catalog product with saved catalog price
{
"type": "product",
"product": {
"product_id": "prod_abc123xyz",
"price_id": "pr_xyz789",
"quantity": 2
}
}
When you reference product_id, Commerce snapshots the product data onto the order at creation time. Later edits to the catalog product or price do not rewrite an order that already exists. You can also mix catalog-backed and inline product line items in the same order, as long as every line item uses the same currency.
The full request examples below use inline product data for broad SDK compatibility. If you already keep products and prices in your catalog, replace the product payload with either catalog-backed shape shown above.
Optional attributes
- Name
custom_data- Type
- object
- Description
Arbitrary string key-value pairs for application-specific needs. Attach internal order IDs, fulfillment workflows, tracking identifiers, or any metadata your system requires. Maximum size when serialized: 25 KB. See the Custom Data guide for best practices.
Request
curl https://api.zebo.dev/orders/new \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_meta": {
"idempotency_key": "order_checkout_ORDER-NUMBER-2"
},
"number": "ORDER-NUMBER-2",
"receipt_number": "RCPT-NUMBER-2",
"statement_descriptor": "STMTDESC",
"execute_payment": true,
"finalize": true,
"checkout_settings": {
"redirect_url": "https://google.com/thank-you",
"cancel_url": "https://google.com/order-cancelled"
},
"customer_data": {
"name": "Customer Name",
"email_address": "[email protected]",
"phone_number": "+233242058841"
},
"payment_method_data": {
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0242057831"
}
},
"line_items": [
{
"type": "product",
"product": {
"type": "physical",
"name": "Utility Sneakers",
"quantity": 1,
"price": {
"currency": "ghs",
"value": 20000
}
}
}
],
"billing_details": {
"name": "Customer Name",
"email_address": "[email protected]",
"address": {
"line1": "23 Adenta High Street",
"city": "Accra",
"country": "GH"
}
}
}'
{
"order": {
"id": "or_f8y1p1",
"number": "ORDER-NUMBER-2",
"receipt_number": "RCPT-NUMBER-2",
"status": "requires_payment",
"statement_descriptor": "STMTDESC",
"initiated_at": "2025-01-13T10:00:00Z",
"sealed_at": "2025-01-13T10:00:01Z",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "prod_utility",
"type": "physical",
"tax_code": "apparel",
"name": "Utility Sneakers",
"price": { "currency": "ghs", "value": 20000 },
"quantity": 1,
"reference": "utility-sneakers"
}
}
],
"total": { "currency": "ghs", "value": 20500 }
},
"payment": {
"id": "py_3deNYy",
"statement_descriptor": "STMTDESC",
"payment_method": {
"id": "pm_wveyHj",
"customer_id": "cu_a1b2c3",
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0242057831"
},
"created_at": "2025-01-13T10:00:00Z",
"verified": false
},
"amount": { "currency": "ghs", "value": 20500 },
"next_action": {
"type": "confirm_payment",
"confirm_payment": {
"confirmed": false,
"expires_at": "2025-01-13T10:08:00Z",
"scheme": "zcommerce_defined_auth",
"request": {
"id": "token_req_1",
"recipient": "0242057831",
"sent_via": "sms",
"token_size": 6,
"sender_id": "zverify"
},
"status": "pending"
}
},
"status": "requires_action",
"initiated_at": "2025-01-13T10:00:00Z"
},
"invoice": {
"format": {
"web": { "url": "https://pages.zebo.dev/invoices/or_f8y1p1" },
"pdf": { "url": "https://pages.zebo.dev/invoices/or_f8y1p1/pdf" }
}
},
"customer": {
"id": "cu_a1b2c3",
"email_address": "[email protected]",
"phone_number": "+233242058841",
"guest": false,
"name": "Customer Name"
}
}
}
Pay for an order
Charge an existing order without recreating it. Four ways to use this:
- Already has a payment method: Just send
order_id—we'll charge the attached method. - Swap to a different saved method: Send
order_id+payment_method_id. - Use a new payment method once: Send
order_id+payment_method_data. - Use and save a new method: Add
save_payment_method: trueand includepayment_method_data.billing_details. - Offline payment: Send
order_id+paid_out_of_band: true—marks the payment as received outside Commerce (cash, bank transfer, check).
Rules
- Pass either
payment_method_id,payment_method_data, orpaid_out_of_band, never multiple. paid_out_of_bandis mutually exclusive with payment method parameters.- Any
payment_method_idmust belong to the order's customer. save_payment_methodaffects only inlinepayment_method_data; it defaults tofalse.
Optional attributes
Request
curl https://api.zebo.dev/orders/pay \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_JoPtqOmsjgZKwkPvqTrqGsopu07wfC7ttoWqmfwt",
"payment_method_data": {
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0544998605"
}
}
}'
{
"order": {
"id": "or_JoPtqOmsjgZKwkPvqTrqGsopu07wfC7ttoWqmfwt",
"initiated_at": "2026-08-28T09:30:00Z",
"status": "requires_payment",
"customer": {
"guest": true,
"id": "cu_4pL8rN",
"name": "Gloria Kesewaa"
},
"payment": {
"amount": { "currency": "ghs", "value": 20000 },
"id": "py_3deNYy",
"initiated_at": "2026-08-28T09:30:01Z",
"statement_descriptor": "ZEBO",
"status": "requires_action"
}
}
}
Confirm a payment
Submit the confirmation token for a specific payment and confirmation request. Read payment_id from order.payment.id and confirmation_id from order.payment.next_action.confirm_payment.request.id in the preceding create, pay, or request-confirmation response. The response always uses the { "order": ... } envelope; an actionable error can include both order and error.
Required attributes
Request
curl https://api.zebo.dev/orders/confirm_payment \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"confirmation_id": "otc_req_8Ks2Vn",
"order_id": "or_JoPtqOmsjgZKwkPvqTrqGsopu07wfC7ttoWqmfwt",
"payment_id": "py_3deNYy",
"token": "302673"
}'
{
"order": {
"customer": {
"guest": true,
"id": "cu_4pL8rN",
"name": "Gloria Kesewaa"
},
"id": "or_JoPtqOmsjgZKwkPvqTrqGsopu07wfC7ttoWqmfwt",
"initiated_at": "2026-08-28T09:30:00Z",
"status": "paid"
}
}
Request confirmation
Some payment methods—especially mobile money—require the customer to confirm with an OTP. Call this to send (or resend) the confirmation token.
Request
curl https://api.zebo.dev/orders/request_confirmation \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_kAVwBsk6EhQVv2YBUhYAqMf0lktEoQ0S7ZR8y2xi"
}'
{
"order": {
"id": "or_kAVwBsk6EhQVv2YBUhYAqMf0lktEoQ0S7ZR8y2xi",
"status": "requires_payment",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "pm_fNa3i3QPkt4yU5FQHoM042tRFAdthCbcRZJdZnrl",
"reference": "pm_fNa3i3QPkt4yU5FQHoM042tRFAdthCbcRZJdZnrl",
"about": "Tollo victoria advenio crudelis facere crudelis. Aequitas voco correptius suspendo fuga demergo. Angelus texo pecto.",
"custom_data": {
"size": "56",
"color": "blue"
},
"tax_code": "gold",
"name": "Awesome Fresh Gloves",
"type": "digital",
"price": {
"currency": "ghs",
"value": 20
},
"quantity": 1
}
},
{
"type": "product",
"product": {
"id": "cu_deeYHxzydDJWU1w9YCjOvgDirYv37WyYmI9MhdEA",
"reference": "cu_deeYHxzydDJWU1w9YCjOvgDirYv37WyYmI9MhdEA",
"about": "Sunt astrum verecundia cenaculum. Asporto voluptas mollitia voluptate vaco concedo bellum clam aufero taedium. Admiratio cohibeo ullus comis alter.",
"custom_data": {
"size": "56",
"color": "blue"
},
"tax_code": "black",
"name": "Awesome Fresh Table",
"type": "digital",
"price": {
"currency": "ghs",
"value": 100
},
"quantity": 2
}
}
],
"total": {
"currency": "ghs",
"value": 220
}
},
"initiated_at": "2025-04-09T00:11:47.74569+01:00",
"sealed_at": "2025-04-09T00:11:48.374979+01:00",
"payment": {
"id": "py_8QNM4ujclxXUDZBnL9ujyPfMG4Bk6ZRMBeRtc99l",
"statement_descriptor": "STATEMENT",
"payment_method": {
"id": "pm_acA9cJRhjZHw1Gs4p39fKTbp1AgsFjhBk1AwIuVf",
"customer_id": "cu_N4IX542ZKixJJhIbc4bQBntdPrRb0DQ7KqEYSyqc",
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "024******31",
"last4": "7831"
},
"created_at": "2025-04-09T00:11:47.873476+01:00",
"verified": false
},
"amount": {
"currency": "ghs",
"value": 220
},
"next_action": {
"type": "confirm_payment",
"confirm_payment": {
"expires_at": "0001-01-01T00:00:00Z",
"scheme": "zcommerce_defined_auth",
"request": {
"recipient": "0242057831",
"sent_via": "sms",
"token_size": 6,
"sender_id": "zverify"
},
"confirmed": false,
"status": "pending_verification"
}
},
"status": "requires_action",
"initiated_at": "2025-04-09T00:11:47.995076+01:00"
},
"customer": {
"id": "cu_N4IX542ZKixJJhIbc4bQBntdPrRb0DQ7KqEYSyqc",
"email_address": "[email protected]",
"phone_number": "+233544998605",
"name": "Gloria Kesewaa"
}
}
}
Update an order
Update mutable fields on an existing order and get back the same response shape as Lookup an order. Use this endpoint for three distinct cases: revising an open draft, reopening a sealed order so it becomes editable again, or applying changes and resealing in the same request.
What you can change
- Replace the full
line_itemscollection - Change the order
number - Set or change the order
receipt_number - Change the payment
statement_descriptor - Attach a different saved
payment_method_id - Tokenize and attach new inline
payment_method_data - Remove the attached payment method with
clear_payment_method - Replace order-level
custom_data - Replace or clear customer-facing
invoice_settings - Explicitly open, seal, or reseal the order with
finalize
Rules
- Include at least one mutable field besides
order_id. line_itemsis a full replacement field, not a sparse merge, and it cannot be empty when provided.payment_method_id,payment_method_data, andclear_payment_methodare mutually exclusive.payment_method_datacurrently supports onlymobile_moneyfor order updates.- Completed, paid, canceled, and expired orders cannot be updated.
custom_datais metadata-only. Updating it does not require reopen / reseal by itself.- Omit
receipt_numberto leave it unchanged. When provided, Commerce trims it and requires a different non-empty value within the same configured length limits asnumber. It cannot be cleared once set, and receipt numbers are not unique. - If payment confirmation or execution has already started, Commerce rejects edits that change economics, payment configuration, or require reopening the order.
- When mutating seal-sensitive fields on a sealed order, you must provide
finalizeexplicitly:finalize: falsereopens the order and leaves it editable.finalize: trueapplies the changes and seals the order again in the same request.
Required attributes
Optional attributes
- Name
line_items- Type
- array
- Description
Full replacement for the order's current line items. Use the same line-item schema as Create an order.
Response shape
- Returns the same
orderobject as Lookup an order. - If
finalize: falsereopens a sealed order,sealed_atandinvoiceare cleared until the order is sealed again. - If
finalize: trueseals or reseals the order, Commerce returns the regenerated invoice and the updated payment amount. - The returned order object includes top-level
custom_datawhen present. - The returned order object includes top-level
receipt_numberwhen set.
Request
curl https://api.zebo.dev/orders/update \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"receipt_number": "RCPT-2025-041-REV1",
"line_items": [
{
"type": "product",
"product": {
"name": "Quarterly support retainer",
"type": "digital",
"price": {
"currency": "ghs",
"value": 15000
},
"quantity": 1
}
},
{
"type": "fee",
"fee": {
"label": "Service fee",
"amount": {
"currency": "ghs",
"value": 500
}
}
}
],
"statement_descriptor": "ORDER REVISION",
"custom_data": {
"channel": "dashboard",
"edited_by": "ops"
},
"finalize": true
}'
{
"order": {
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"number": "ORDER-2025-REV2",
"receipt_number": "RCPT-2025-041-REV1",
"status": "requires_payment",
"initiated_at": "2025-05-01T10:00:00Z",
"sealed_at": "2025-05-01T10:04:00Z",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "li_VNojv2eX7F9H3mQ5pZr2Ld8",
"name": "Quarterly support retainer",
"type": "digital",
"price": {
"currency": "ghs",
"value": 15000
},
"quantity": 1
}
},
{
"type": "fee",
"fee": {
"id": "li_7Nf0wP4sT8kJ2qM6xCz9Rb1",
"label": "Service fee",
"amount": {
"currency": "ghs",
"value": 500
}
}
}
],
"total": {
"currency": "ghs",
"value": 15500
}
},
"payment": {
"id": "py_3PjQ7mVn2cX5kL8rT1sW4yH6uB9dF0gN",
"statement_descriptor": "ORDER REVISION",
"payment_method": {
"id": "pm_hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"customer_id": "cu_ewoa27qe7aO4GZouasUSmVXILsmxjR0Hc6lCMBkn",
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0594870087"
},
"created_at": "2025-05-01T10:00:00Z",
"verified": true,
"verified_at": "2025-05-01T10:00:15Z"
},
"amount": {
"currency": "ghs",
"value": 15500
},
"status": "requires_action",
"initiated_at": "2025-05-01T10:00:00Z"
},
"invoice": {
"id": "in_bRSGJcH7v4oLIAa3KlBmB9K6ZoRUucJXAJMWxl1KQq0JCZs0oOEG8ibCeGn1",
"format": {
"web": {
"url": "https://pages.zebo.dev/invoices/or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
},
"pdf": {
"url": "https://pages.zebo.dev/invoices/or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt/pdf"
}
}
},
"customer": {
"id": "cu_ewoa27qe7aO4GZouasUSmVXILsmxjR0Hc6lCMBkn",
"email_address": "[email protected]",
"phone_number": "+233559714200",
"name": "Anita Segnom"
},
"custom_data": {
"channel": "dashboard",
"edited_by": "ops"
}
}
}
Finalize an order
Finalize an order to seal it for payment. This endpoint generates an invoice and hosted checkout page that you can share with customers.
What finalization means
Finalizing an order is the transition from draft to ready-for-payment. Before finalization, orders are flexible—you can add products, adjust quantities, or update customer details. After finalization:
- Line items freeze: No additions, removals, or quantity changes unless you later reopen the order with Update an order
- Totals lock: The amount due becomes fixed until the order is reopened
- Invoice generates: A viewable invoice with web and PDF formats
- Checkout activates: The hosted payment page becomes accessible
- Payment readiness: The order can accept payment attempts
Use this endpoint when you've finished building the cart and want to present it to the customer for payment. The response includes invoice URLs and the sealed_at timestamp marking when the order was finalized.
Required attributes
Request
curl https://api.zebo.dev/orders/finalize \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
{
"order": {
"number": "ORDER-12345",
"status": "requires_payment",
"payment_id": "py_xyz123abc456",
"customer_id": "cu_abc123def456",
"sealed_at": "2025-01-25T10:30:00Z",
"initiated_at": "2025-01-25T10:25:00Z",
"expires_at": "2025-02-01T10:25:00Z",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "prod_123",
"name": "Premium Widget",
"quantity": 2,
"price": {
"currency": "ghs",
"value": 5000
}
}
}
],
"total": {
"currency": "ghs",
"value": 10000
}
},
"invoice": {
"number": "ORDER-12345",
"due_at": "2025-01-25T10:30:00Z"
}
}
}
Send an invoice
Send the hosted invoice link for an existing order. Commerce uses the order customer's contact details: if the customer has both phone and email, it sends SMS and email; otherwise it sends to whichever contact method exists.
This endpoint is idempotent. Send a stable idempotency key when retrying so Commerce can replay the original delivery result instead of creating duplicate messages.
The order must have a hosted invoice before delivery. Finalize draft orders first with Finalize an order. If a sealed order is missing its invoice, Commerce may generate the invoice before sending.
Request body
Response
Returns the current order plus a delivery object. If one channel succeeds and another fails, the endpoint may return HTTP 207 with both sent_channels and failed_channels.
Invoice delivery can return draft_mode_order_has_no_invoice, order_delivery_auth_failed, order_delivery_contact_missing, order_delivery_failed, or order_not_found.
Request
curl https://api.zebo.dev/orders/send_invoice \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: send-invoice-or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
{
"order": {
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"status": "requires_payment"
},
"delivery": {
"document_kind": "invoice",
"document_url": "https://pages.zebo.dev/invoices/or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"sent_channels": ["sms", "email"],
"deliveries": [
{
"channel": "sms",
"chime_id": "chm_sms_123"
},
{
"channel": "email",
"chime_id": "chm_email_456"
}
]
}
}
Send a receipt
Send the hosted receipt link for an existing paid order. Commerce uses the same fan-out rules as invoice delivery: SMS and email when both customer contacts exist, otherwise whichever contact method is available.
Receipt delivery is available only after the order is paid. The link points to the hosted receipt path, such as /invoices/{order_id}/receipt; do not use the invoice PDF path for receipt delivery.
This endpoint is idempotent. Send a stable idempotency key when retrying.
Request body
Response
Returns the current order plus a delivery object. If one channel succeeds and another fails, the endpoint may return HTTP 207 with both sent_channels and failed_channels.
Receipt delivery can return order_delivery_auth_failed, order_delivery_contact_missing, order_delivery_failed, order_not_found, or order_receipt_missing.
Request
curl https://api.zebo.dev/orders/send_receipt \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: send-receipt-or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
{
"order": {
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"status": "paid"
},
"delivery": {
"document_kind": "receipt",
"document_url": "https://pages.zebo.dev/invoices/or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt/receipt",
"sent_channels": ["sms"],
"deliveries": [
{
"channel": "sms",
"chime_id": "chm_sms_789"
}
]
}
}
Complete an order
Mark an order as completed when the customer has received their items or you've fulfilled the service. This transitions the order to the completed state, indicating the transaction is fully satisfied.
When to complete orders
Complete an order when:
- Physical goods: Items have been delivered or picked up
- Digital products: Files have been downloaded or access granted
- Services: Work has been performed and accepted
- Out-of-band payments: Cash, check, or bank transfer received offline
The order must have a successful payment before completion, unless you're marking an offline payment with paid_out_of_band: true.
Out-of-band payments
If the customer paid outside Commerce (cash, bank transfer, check), set paid_out_of_band: true. This marks both the payment and order as complete in a single operation. The payment gets marked as paid offline before the order is completed.
Use this for:
- Cash on delivery scenarios
- Bank transfer confirmations
- Check payments that cleared
- Any payment method outside the Commerce platform
Required attributes
Optional attributes
Request
curl https://api.zebo.dev/orders/complete \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
{
"order": {
"number": "ORDER-12345",
"status": "completed",
"payment_id": "py_xyz123abc456",
"customer_id": "cu_abc123def456",
"completed_at": "2025-01-27T11:00:00Z",
"sealed_at": "2025-01-25T10:30:00Z",
"initiated_at": "2025-01-25T10:25:00Z",
"expires_at": "2025-02-01T10:25:00Z",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "prod_123",
"name": "Premium Widget",
"quantity": 2,
"price": {
"currency": "ghs",
"value": 5000
}
}
}
],
"total": {
"currency": "ghs",
"value": 10000
}
}
}
}
Lookup an order
Fetch the current state of an order by ID. Returns the full order object or a 404 if it doesn't exist.
Required attributes
Request
curl https://api.zebo.dev/orders/lookup \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
{
"order": {
"line_item_group": {
"line_items": [
{
"product": {
"id": "pm_BydaofEe1IdJro0BVjlm89PxeENo8GsqX09yVtBL",
"about": "Accedo turba doloremque brevis. Despecto administratio vorax verus odit vinculum. Advoco conforto spiritus debeo careo.",
"custom_data": {
"size": "56",
"some": "custom_data"
},
"tax_code": "pink",
"name": "Gorgeous Granite Salad",
"type": "physical",
"price": {
"currency": "ghs",
"value": 100
},
"quantity": 1
},
"type": "product"
}
],
"total": {
"currency": "ghs",
"value": 200
}
},
"initiated_at": "2025-09-14T16:24:41.137708909Z",
"completed_at": "2025-09-14T16:29:45Z",
"sealed_at": "2025-09-14T16:24:41.312701813Z",
"payment": {
"id": "py_OHFv6I3OZBvB1R46TQQGNxPPtgbPR6CXpQ8En9wl",
"statement_descriptor": "STATEMENT",
"payment_method": {
"id": "pm_hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"customer_id": "cu_ewoa27qe7aO4GZouasUSmVXILsmxjR0Hc6lCMBkn",
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0594870087"
},
"created_at": "2025-09-14T16:24:41.171905592Z",
"verified": true,
"verified_at": "2025-09-14T16:24:51.171905592Z"
},
"latest_attempt": {
"payment_method_type": "mobile_money",
"payment_method_id": "hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"reference": "68c6ecbc3bf0a6548973b598",
"status": "succeeded",
"initiated_at": "2025-09-14T16:26:36.627271781Z",
"succeeded_at": "2025-09-14T16:29:45Z"
},
"amount": {
"currency": "ghs",
"value": 200
},
"status": "paid",
"initiated_at": "2025-09-14T16:24:41.240462376Z",
"executed_at": "2025-09-14T16:26:36.626961042Z",
"paid_at": "2025-09-14T16:29:45Z",
"balance_transaction": {
"id": "bt_EWOA27qe7aO4GZouasUSmVXILsmxjR0H",
"payout_id": "po_68c6ecbc3bf0a6548973b598",
"payment_id": "py_OHFv6I3OZBvB1R46TQQGNxPPtgbPR6CXpQ8En9wl",
"created_at": "2025-09-14T16:29:45Z",
"paid_at": "2025-09-15T08:00:00Z",
"amount": {
"currency": "ghs",
"value": 200
}
}
},
"invoice": {
"id": "in_bRSGJcH7v4oLIAa3KlBmB9K6ZoRUucJXAJMWxl1KQq0JCZs0oOEG8ibCeGn1",
"format": {
"web": {
"url": "https://pages.zebo.dev/invoices/48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
},
"pdf": {
"url": "https://pages.zebo.dev/invoices/48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt/pdf"
}
}
},
"customer": {
"id": "cu_ewoa27qe7aO4GZouasUSmVXILsmxjR0Hc6lCMBkn",
"email_address": "[email protected]",
"phone_number": "+233559714200",
"name": "Anita Segnom"
},
"status": "completed",
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}
}
Cancel an order
Cancel an order that has not yet been completed. Cancellation marks the order as canceled and records the reason and timestamp. If the order has an associated payment that has already been processed, pass execute_refund: true to trigger a refund at the same time.
You cannot cancel an order that has already reached completed status—use a refund flow for post-completion reversals. Orders in requires_payment status (mid-payment-flow) can be canceled; the associated payment will be voided.
Request attributes
Request
curl https://api.zebo.dev/orders/cancel \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_kAVwBsk6EhQVv2YBUhYAqMf0lktEoQ0S7ZR8y2xi",
"reason": "customer_request",
"execute_refund": false
}'
{
"order": {
"id": "or_kAVwBsk6EhQVv2YBUhYAqMf0lktEoQ0S7ZR8y2xi",
"status": "canceled",
"initiated_at": "2025-04-09T00:11:47.74569+01:00",
"line_item_group": {
"line_items": [
{
"type": "product",
"product": {
"id": "pr_ZLxM9pQ4wRsN7jKv2cY1tD8hBgFnAo3i",
"name": "Tailoring Service",
"price": {
"currency": "ghs",
"value": 200
},
"quantity": 1
}
}
],
"total": {
"currency": "ghs",
"value": 200
}
},
"payment": {
"id": "py_N4IX542ZKixJJhIbc4bQBntdPrRb0DQ7KqEYSyqc",
"status": "canceled",
"amount": {
"currency": "ghs",
"value": 200
},
"canceled_at": "2025-04-09T01:05:12Z",
"initiated_at": "2025-04-09T00:11:48Z"
}
}
}
Page through orders
Retrieve a paginated list of the most recent orders for the authenticated application. Orders are sorted by initiated_at in descending order, so page 0 contains the freshest activity and subsequent pages step back in time.
Provide customer_id to scope the page to a single customer. Omit customer_id to browse all recent orders across the application.
Required attributes
Optional attributes
Response shape
- Top level
pageobject includesnumber,size, and anordersarray. - Each array entry uses the complete shared order object, not a condensed summary.
- Optional fields are omitted when unavailable. The API does not add
payment.receiptorline_item_group.products_countsummary fields. - When you filter by
customer_id, every returned order belongs to that customer.
Request
curl https://api.zebo.dev/orders/page \
-H "Authorization: Bearer $COMMERCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"page_number": 0,
"page_size": 25
}'
{
"page": {
"number": 0,
"size": 2,
"orders": [
{
"id": "or_kAVwBsk6EhQVv2YBUhYAqMf0lktEoQ0S7ZR8y2xi",
"line_item_group": {
"line_items": [],
"total": {
"currency": "ghs",
"value": 220
}
},
"initiated_at": "2025-04-09T00:11:47.74569+01:00",
"sealed_at": "2025-04-09T00:11:48.374979+01:00",
"status": "requires_payment",
"customer": {
"id": "cu_N4IX542ZKixJJhIbc4bQBntdPrRb0DQ7KqEYSyqc",
"guest": false,
"name": "Gloria Kesewaa",
"email_address": "[email protected]",
"phone_number": "+233544998605"
},
"payment": {
"id": "py_hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"amount": { "currency": "ghs", "value": 220 },
"initiated_at": "2025-04-09T00:11:47.842341+01:00",
"statement_descriptor": "ZEBO",
"status": "requires_action",
"payment_method": {
"id": "pm_hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"customer_id": "cu_N4IX542ZKixJJhIbc4bQBntdPrRb0DQ7KqEYSyqc",
"created_at": "2025-04-09T00:11:47.842341+01:00",
"verified": true,
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "+233******8605",
"last4": "8605"
}
},
"latest_attempt": {
"payment_method_type": "mobile_money",
"payment_method_id": "pm_hg7D2A5qT6g6mVDSJu6zweKXwsiiNWf3Q85book6",
"reference": "nsn_57d91e4fd4b8",
"status": "executed",
"initiated_at": "2025-04-09T00:11:48.391412+01:00"
}
}
},
{
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"line_item_group": {
"line_items": [],
"total": {
"currency": "ghs",
"value": 200
}
},
"initiated_at": "2025-09-14T16:24:41.137708909Z",
"completed_at": "2025-09-14T16:29:45Z",
"sealed_at": "2025-09-14T16:24:41.312701813Z",
"status": "completed",
"customer": {
"id": "cu_ewoa27qe7aO4GZouasUSmVXILsmxjR0Hc6lCMBkn",
"guest": false,
"name": "Anita Segnom",
"email_address": "[email protected]",
"phone_number": "+233559714200"
}
}
]
}
}