Gett Developer Portal
  • Welcome
  • Distribution Partners
  • Brand Partners
  • Commerce Partners
  • Ecosystem Partners
  • Errors
  • API Reference
Documentation
  • Get Started
  • Marketfront SDK
  • API Reference
Resources
  • Payments
Company
  • Gett
  • Terms of Service
  • Privacy Policy

Copyright 2026 Gett. All rights reserved.

Getting StartedCatalog IntegrationOrder WebhooksStore Management
API Reference
powered by Zuplo
Commerce Partners

Order Webhooks

Gett drives the order lifecycle by POSTing synchronously to two endpoints on your system. You respond with the order body (including your server-computed amounts); Gett waits for your reply before continuing. These are not fire-and-forget events — every delivery is a blocking RPC.

These Commerce API webhooks are a separate system from the Distribution Partner async event webhooks. They use a different signing scheme (Standard Webhooks) and a different interaction model (synchronous request/response vs. fire-and-forget).


Endpoints

Gett POSTs to {baseUrl}/validate and {baseUrl}/place, where baseUrl is the Webhook Base URL you provide during onboarding.

PathPurposeWhen called
{baseUrl}/validatePrice and availability checkBefore the customer pays
{baseUrl}/placePlace the orderAfter successful payment

Both endpoints receive the same order body shape (see Request body). The /place body additionally carries paymentToken when Gett tokenized the card on the partner's behalf (see Payment token).


Request body

The request body is the order represented as a StandardOrderWebhook object — an ExternalOrder extended with an optional paymentToken. Field-level schemas live in the Commerce API Reference: ExternalOrder (the /validate body) and StandardOrderWebhook (the /place body). The validateOrder / placeOrder summaries are also listed under Webhooks on the reference overview.

Validate — example request body

All IDs are UUIDs. Monetary amounts are decimal major units (e.g. 24.00 = $24.00) — not minor units. Every lineItem carries a modifierGroups array (empty when the item has no modifiers).

Code
{ "gettOrderId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c", "cart": { "storeId": "7c3a1e90-2b4d-4f8a-9c1e-5a6b7c8d9e0f", "catalogSetId": "3f2504e0-4f89-41d3-9a0c-0305e82c3301", "lineItems": [ { "itemId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "quantity": 2, "modifierGroups": [ { "modifierGroupId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "lineItems": [ { "itemId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "quantity": 1, "modifierGroups": [] } ] } ] } ] }, "amounts": { "currency": "USD", "subTotal": "24.00", "tip": "3.00" }, "fulfillmentType": "PICKUP", "customer": { "firstName": "Alex", "lastName": "Kim", "email": "[email protected]", "phone": "+14155552671" } }

Place — example request body

Identical to validate, plus paymentToken when Gett performed Braintree tokenization:

Code
{ "gettOrderId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c", "cart": { "...": "same as validate" }, "amounts": { "currency": "USD", "subTotal": "24.00", "tip": "3.00" }, "fulfillmentType": "PICKUP", "customer": { "...": "same as validate" }, "paymentToken": "tokencc_bf_k8x2m9_single_use_nonce" }

Payment token

paymentToken is a Braintree single-use payment-method nonce. It is present on /place only when your store has a Braintree tokenization key configured — Gett tokenizes the customer's saved card against your Braintree account and sends you the nonce to charge. When absent, payment falls back to your own tokenization flow.

Treat the payment token as a secret

The nonce is a short-lived, single-use payment credential — usable for up to a few hours until it is consumed. Use it once over TLS to charge, then discard it. Gett masks paymentToken in its own logs and distributed traces, and you must do the same: never write the raw value to logs, traces, or analytics. If you need a reference for reconciliation, keep only non-sensitive descriptors (e.g. card type or last four), never the nonce itself.


Response body

Respond with the same order shape, filling in the server-authoritative amounts and any errors. HTTP 200 with a valid body is the success signal; any non-2xx triggers Gett's retry policy.

Amounts ownership

FieldOwnerRule
subTotalGettEcho verbatim
tipGettEcho verbatim
feesPartnerSet authoritatively; Gett stores your value
taxesPartnerSet authoritatively; Gett stores your value

Example success response

Code
{ "gettOrderId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c", "partnerOrderId": "POS-99012", "cart": { "...": "echo the cart" }, "amounts": { "currency": "USD", "subTotal": "24.00", "tip": "3.00", "fees": "1.50", "taxes": "1.98", "total": "30.48" }, "fulfillmentType": "PICKUP", "errors": [] }

Example error response (still HTTP 200)

Return errors inline on 200 — do not use 4xx for business-logic rejections (item unavailable, store closed, etc.). Reserve non-2xx for infrastructure failures.

Code
{ "gettOrderId": "018f4d2a-c5b0-7c4e-9b3a-2d1e8f7a6b5c", "cart": { "...": "echo the cart" }, "amounts": null, "fulfillmentType": "PICKUP", "errors": [ { "code": "ITEM_UNAVAILABLE", "message": "This item is not available at this time.", "itemId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "jsonPath": "$.cart.lineItems[0]" } ] }

Security — Standard Webhooks signing

Gett signs every outbound webhook per the open Standard Webhooks spec. Because the spec is open, you can verify signatures using off-the-shelf libraries (e.g. standardwebhooks on npm, standardwebhooks on PyPI) rather than writing verification code by hand.

Headers Gett sends

HeaderValue
AuthorizationBearer <apiKey> — authenticates Gett to your endpoint
Idempotency-KeyStable GUID for this delivery (reused across retries; use it for dedup)
webhook-idStable message ID (same value as Idempotency-Key for this delivery)
webhook-timestampUnix timestamp in seconds of this delivery attempt
webhook-signatureSpace-delimited list of v1,<base64> signatures (see below)

Signature algorithm

The signed content is:

Code
{webhook-id}.{webhook-timestamp}.{rawRequestBody}

For each active signing secret, Gett computes:

Code
v1,base64( HMAC-SHA256( key, signedContent ) )

where key is the base64-decoded bytes of the part after whsec_ in your secret (e.g. if your secret is whsec_ABC123==, the HMAC key is base64Decode("ABC123==")).

The webhook-signature header contains one v1,<base64> entry per active secret, space-delimited. This enables zero-downtime secret rotation: during a rotation overlap window both the old and new secret produce a signature; your verifier accepts whichever one it currently holds.

Verifying with a library (recommended)

Code
import { Webhook } from 'standardwebhooks'; const wh = new Webhook(signingSecret); // signingSecret = your whsec_... value app.post('/validate', express.raw({ type: 'application/json' }), (req, res) => { const payload = wh.verify(req.body, { 'webhook-id': req.headers['webhook-id'] as string, 'webhook-timestamp': req.headers['webhook-timestamp'] as string, 'webhook-signature': req.headers['webhook-signature'] as string, }); // payload is the parsed order; process and respond res.json({ ...payload, amounts: { ...payload.amounts, fees: 150, taxes: 198 } }); });

Verifying manually (TypeScript / Node.js)

Code
import crypto from 'crypto'; function verifyStandardWebhook( rawBody: Buffer, webhookId: string, webhookTimestamp: string, webhookSignature: string, signingSecret: string, // your whsec_... value toleranceSeconds = 300, ): boolean { // Replay protection const ts = parseInt(webhookTimestamp, 10); if (Math.abs(Date.now() / 1000 - ts) > toleranceSeconds) return false; // Decode the HMAC key: strip 'whsec_' prefix, then base64-decode const prefix = 'whsec_'; const b64 = signingSecret.startsWith(prefix) ? signingSecret.slice(prefix.length) : signingSecret; const key = Buffer.from(b64, 'base64'); const signedContent = `${webhookId}.${webhookTimestamp}.${rawBody}`; const expected = crypto .createHmac('sha256', key) .update(signedContent) .digest('base64'); // webhook-signature may contain multiple space-delimited v1,<base64> entries return webhookSignature .split(' ') .some(entry => { const sig = entry.startsWith('v1,') ? entry.slice(3) : entry; try { return crypto.timingSafeEqual( Buffer.from(sig, 'base64'), Buffer.from(expected, 'base64'), ); } catch { return false; } }); }

Verifying manually (Python)

Code
import base64 import hashlib import hmac import time def verify_standard_webhook( raw_body: bytes, webhook_id: str, webhook_timestamp: str, webhook_signature: str, signing_secret: str, # your whsec_... value tolerance_seconds: int = 300, ) -> bool: # Replay protection ts = int(webhook_timestamp) if abs(time.time() - ts) > tolerance_seconds: return False # Decode the HMAC key: strip 'whsec_' prefix, then base64-decode prefix = "whsec_" b64 = signing_secret[len(prefix):] if signing_secret.startswith(prefix) else signing_secret key = base64.b64decode(b64) signed_content = f"{webhook_id}.{webhook_timestamp}.".encode() + raw_body expected = base64.b64encode( hmac.new(key, signed_content, hashlib.sha256).digest() ).decode() # webhook-signature may contain multiple space-delimited v1,<base64> entries for entry in webhook_signature.split(" "): sig = entry[3:] if entry.startswith("v1,") else entry if hmac.compare_digest(sig, expected): return True return False

Always verify signatures

Never process an order webhook without verifying the signature and checking the timestamp. Reject requests with missing, invalid, or stale signatures with a 401 response.


Idempotency

Gett retries failed deliveries. Use Idempotency-Key (or webhook-id — they carry the same value) to deduplicate: if you have already processed this key, return 200 with the previously computed result rather than placing the order again.


Retry policy

Gett retries failed webhook deliveries with exponential backoff:

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
4th retry1 hour
Final retry6 hours

A delivery is considered failed if your endpoint returns a non-2xx status code or does not respond within 30 seconds.


Best practices

  • Respond as quickly as possible; the timeout is 30 seconds
  • Always verify the signature before reading the body
  • Use Idempotency-Key / webhook-id to deduplicate retried deliveries
  • Return inline errors on HTTP 200 for business-logic rejections; reserve non-2xx for infrastructure failures
  • Log the full request including all headers for debugging
Catalog IntegrationStore Management
On this page
  • Endpoints
  • Request body
    • Validate — example request body
    • Place — example request body
  • Response body
    • Amounts ownership
    • Example success response
    • Example error response (still HTTP 200)
  • Security — Standard Webhooks signing
    • Headers Gett sends
    • Signature algorithm
    • Verifying with a library (recommended)
    • Verifying manually (TypeScript / Node.js)
    • Verifying manually (Python)
  • Idempotency
  • Retry policy
  • Best practices
JSON
JSON
JSON
JSON
TypeScript
TypeScript