API Reference
This reference summarizes the public contracts used by GOAT Flow integrations. Prefer the Server SDK for authenticated requests so signing, timestamps, nonces, and response normalization remain consistent.
Environments
Production and test are separate environments. They use separate merchant IDs, credentials, receiving wallets, links, orders, products, webhook secrets, and chain/token configuration.
| Environment | API URL | Merchant Portal |
|---|---|---|
| Production | https://flow-api.goat.network | https://flow-merchant.goat.network |
| Test | https://flow-api.testnet3.goat.network | https://flow-merchant.testnet3.goat.network |
Do not infer supported chains, token contracts, decimals, limits, or payment methods from examples. Read them from the active environment and merchant configuration.
Authentication
Authenticated merchant requests under /api/v1 use HMAC-SHA256. This scheme
signs parameters, not the raw JSON body; it is different from
Webhook verification.
| Header | Contract |
|---|---|
X-API-Key | Merchant API key in the selected environment |
X-Timestamp | Decimal Unix timestamp in seconds; the server permits at most 300 seconds of clock difference in either direction |
X-Nonce | Fresh random value for every request, including retries; use a UUID. Accepted length after trimming is 8–128 bytes |
X-Sign | HMAC-SHA256 digest encoded as lowercase hexadecimal, without a sha256= prefix |
Canonical Parameters
- Collect decoded query parameters and, for POST/PUT, the top-level JSON body fields. Body fields override same-named query fields; avoid duplicate names.
- Convert scalar values to strings. Send monetary amounts as decimal strings
and numeric identifiers/expiries as integers. Omit absent optional fields
rather than sending
null. - Add
api_key,timestamp, andnonceusing the exact corresponding header values; these overwrite same-named business parameters. - Remove the
signfield and entries whose value is the empty string"". Do not drop zero orfalsevalues. - Sort parameter names in ASCII order and join as
key=value&key=value. Do not URL-encode this final string or include the HTTP method/path. - HMAC the UTF-8 bytes of that string with the merchant API secret and encode the result as lowercase hexadecimal.
Checkout arrays and objects must first be serialized into the documented
JSON-string fields, such as line_items_json. Sign the resulting string
value exactly as transmitted, not a nested object or a re-serialized variant.
See the SDK-to-HTTP mapping.
The current legacy canonical format does not escape & or = inside values,
so unrestricted scalar values can make its representation ambiguous. Do not
invent a different escaping scheme in one client; coordinate any protocol
change with the platform and avoid unreviewed signing inputs. Non-integer JSON
numbers also have backend-specific formatting; use decimal strings for prices.
This server-side helper covers flat strings, booleans, and safe integers:
Sign the actual flattened HTTP field names, not SDK camelCase parameters.
Generate fresh headers immediately before each authenticated attempt: a
previous nonce may already have been consumed even if the handler returned an
error. Reusing it returns 401 Replay request detected, not 409. Fresh
authentication does not make creating another order/session safe after an
unknown result; the operation's retry contract still applies.
Keep credentials on the server and synchronize its clock.
Merchant Configuration
Use merchant configuration to discover payment capabilities enabled in the selected environment. A token entry can include:
| Field | Meaning |
|---|---|
chain_id | Payment chain identifier |
token_symbol | Supported token symbol |
token_contract | Token contract on that chain |
If token decimals are absent, resolve them from reviewed local metadata or the token contract. Keep that data scoped to the selected environment.
Orders
Create an Order
Common request fields:
| Field | Type | Required | Notes |
|---|---|---|---|
dapp_order_id | string | Yes | Your stable internal order identifier; ordinary duplicates are rejected, not replayed |
chain_id | number | Yes | Payment chain selected from current configuration |
token_symbol | string | Yes | Configured token symbol |
token_contract | string | Environment-dependent | Configured token contract |
from_address | string | Yes | Payer address |
amount_wei | string | Yes | Integer amount in the token's smallest unit |
merchant_id | string | Environment-dependent | Merchant identifier |
HTTP 402 Payment Required is the expected response when an order is created
and payment is required. It is not an application failure. The response
contains the order identifier and authoritative payment terms.
Server SDK 0.4.0 does not accept or send idempotencyKey on ordinary order
creation. Repeating dapp_order_id returns an order already exists error,
not the original order. Save the returned order ID and payment requirements
against your business record. If creation has an unknown outcome, reconcile
the original attempt before creating another payable order; see
application request and order recovery.
Read Status
Get Payment Record
Use the returned record for reconciliation. Verify its transaction independently
on-chain when your trust model requires chain evidence. The historical
signature field is an unsigned hash of selected payment fields, not a
platform private-key signature or a signed MPP Payment-Receipt; see
payment-record and receipt limits.
Cancel an Eligible Order
Cancellation applies only to eligible Crypto orders still in
CHECKOUT_VERIFIED. FIAT_STRIPE orders are rejected through this endpoint
in every state and must use the Stripe-aware lifecycle. This is not a refund
API and does not reverse a transfer already submitted on-chain. See
SDK cancellation limits.
Checkout Sessions
Checkout Sessions bind server-authoritative price, line items, metadata, and redirect URLs to an opaque checkout identifier. Give the browser the returned ID, URL, and expiry, not merchant credentials.
Create a Checkout Session
This endpoint requires merchant HMAC authentication. The merchant is derived
from the API key, not a request-body merchant_id. Public integrations use
checkout_type: "DIRECT".
| SDK parameter | HTTP JSON field and type | Contract |
|---|---|---|
checkoutType | checkout_type: string | Required; use "DIRECT" |
paymentRails | payment_rails: string | ['crypto', 'fiat'] becomes "crypto,fiat", not a JSON array. Omission defaults to Crypto; explicitly select the intended rails |
price | price: decimal string | Required when Crypto is offered; omit for a Card-only session |
fiatCurrency | fiat_currency: string | Required with fiat; use a supported three-letter currency code |
fiatAmount | fiat_amount: positive decimal string | Required with fiat; independent of the Crypto quote, with no automatic FX conversion |
clientReferenceId | client_reference_id: string | Optional merchant correlation reference, trimmed and limited to 200 characters; same-merchant duplicates conflict |
expiresIn | expires_in: integer seconds | Requested lifetime; use returned expires_at, including Card-specific effective expiry and initialization rules |
lineItems | line_items_json: string | JSON-serialize the array once into this field |
publicMetadata | public_metadata_json: string | JSON-serialize an object; exposed publicly, so exclude secrets and private customer data. Reserved keys may be rejected |
privateMetadata | private_metadata_json: string | JSON-serialize an object; not part of the public session view |
successUrl / cancelUrl | success_url / cancel_url: string | Optional; must match the merchant's preconfigured return-URL allowlist |
Example JSON body offering both payment methods:
The escaped inner JSON is a string value in the outer request. Sign that
decoded string value as line_items_json; do not send line_items as a nested
array. Use the Crypto prerequisites
and Card prerequisites for each
enabled method.
Success is HTTP 200, not the ordinary order-create 402 response:
The response values above are illustrative. Persist the actual returned ID, URL, and Unix-second expiry against the purchase. A Checkout ID is not a payment order ID; the associated order is created during payment initiation.
Read a Checkout Session
These are public, handle-keyed endpoints, not merchant-HMAC endpoints. Treat the high-entropy Checkout ID as a bearer capability and keep it out of logs and unrelated customers' responses. Read it only for the purchase to which your application saved it.
The first endpoint returns the safe public terms and session view. The second returns a compact projection, for example:
status is the session status; order_status is the associated order
status. order_id and order_status can be absent before an order is linked.
tx_hash is optional and is not a Card settlement identifier. A status read can
return HTTP 200 with a terminal session status; HTTP 410 instead describes a
refused operation on a session, not every read of an expired session.
SDK Example
The following is a Crypto-only DIRECT session; first satisfy the
Crypto session prerequisites.
An existing non-empty client_reference_id for the same merchant causes HTTP
409; repeating the create request does not recover the original session. Save
and reuse an eligible session and follow the
recovery rules
after an unknown outcome.
Optional success_url and cancel_url must match the merchant's preconfigured
redirect allowlist.
Omit them when return navigation is not needed. For
Card session parameters, include
the fiat rail and a fiat quote, and follow the
effective expiry and Card initialization rules.
Treat the session ID and URL as bearer capabilities. Do not include secrets or private customer data in public metadata.
Status Values
Order Status
GET /api/v1/orders/{order_id} returns an order's status under merchant HMAC
authentication. Do not compare this value with Checkout COMPLETED.
| Order status | Meaning | Fulfillment |
|---|---|---|
CHECKOUT_VERIFIED | Order created and awaiting payment | Do not fulfill |
PAYMENT_CONFIRMED | Payment evidence is confirmed, but platform processing or fulfillment restrictions may remain | Not a universal permission to fulfill; Card may be parked here for conflict/refund handling |
INVOICED | Platform order completion | Use as the completed-order gate, then check purchase ownership, terms, current business eligibility, and idempotent delivery |
FAILED | Terminal failure | Do not fulfill |
EXPIRED | Payment window expired | Reconcile before any retry |
CANCELLED | Order cancelled while eligible | Do not fulfill |
For Card, a real charge can be booked while fulfillment is withheld because
of a duplicate payment, payment conflict, refund, dispute, or related balance
condition. Such orders can remain at PAYMENT_CONFIRMED; payment confirmation
and payment-level notifications do not override that restriction. Do not try
to bypass a platform hold or treat it as a reason to collect another payment.
Use the flow's completed-order/session state and applicable authenticated fulfillment event, together with your own purchase checks. A completed order does not guarantee that a later refund or dispute can never occur.
A poller may observe INVOICED without first observing
PAYMENT_CONFIRMED. Design state handling for valid transitions rather than
assuming every intermediate state is visible.
Checkout Session Status
GET /checkout/v1/sessions/{checkout_id}/status returns these values in
status, separately from its optional order_status:
| Session status | Meaning | Application action |
|---|---|---|
OPEN | Session is open; Card may also retain this value while its associated payment is processing or held | Not proof of being unpaid; inspect the associated order and existing attempt before offering another payment |
BOUND | A payer/payment order has been linked in the Crypto flow | Not completion; query the same purchase and order |
COMPLETED | Associated order has reached INVOICED | Apply merchant purchase checks and deliver idempotently |
EXPIRED | Session/payment window is expired | Do not start a new payment on this session; reconcile any already-submitted payment |
CANCELLED | Session is cancelled | Do not start another payment on it; cancellation is not a refund |
FAILED | Associated order failed | Do not fulfill; reconcile the original attempt |
The wire format also retains a compatibility-only SIGNED value. It is not
a public DIRECT integration step and does not indicate completion. Do not
add another signing flow merely because this value exists.
Card does not need to pass through BOUND. COMPLETED is derived from
INVOICED, not simply from PAYMENT_CONFIRMED; a held Card payment must not
be fulfilled by treating the two state machines as interchangeable. QuickPay's
separate Crypto session API has its own state contract; see
QuickPay payment status.
QuickPay Payment Status
Crypto and Card use different session types and status values:
| Payment method | Session identifier and confirmed/completed state | Associated payment order |
|---|---|---|
| Crypto | QuickPay Crypto session_id: PAYMENT_CONFIRMED | Order PAYMENT_CONFIRMED or INVOICED maps to the confirmed Crypto session state |
| Card | Checkout checkout_id: COMPLETED | Checkout completion is reported when the order reaches INVOICED; payment confirmation and session completion are separate states |
For Card, your backend can read
GET /checkout/v1/sessions/{checkout_id}/status to obtain status, order_id,
and order_status when bound. Do not compare Card session status only with
PAYMENT_CONFIRMED, or compare an order status with COMPLETED.
Apply the order completion rules and payment and fulfillment event rules to Card; payment confirmation alone does not authorize fulfillment.
Fulfill from a verified backend status or an authenticated webhook available for that payment method, not from hosted-page copy, CLI output, a link-creation response, or a frontend callback alone. Bind the session and order to your internal purchase, and verify the merchant, product or reference, amount, and currency. For Crypto, also verify the chain and Token.
For an any-amount link, confirmation proves the amount paid; it does not prove that the amount matches a product or invoice. Reconcile the amount and your own reference before fulfillment. Use a fixed-price product or server-created Checkout Session when the price must be authoritative.
Webhooks
Webhooks provide event-driven updates for integrations that enable them. They can support Checkout, QuickPay, and API Payments; they are not restricted to a single solution.
Delivery Contract
The platform POSTs a JSON envelope to the configured merchant Webhook URL:
This is a minimal envelope illustration, not a complete order.invoiced
payload schema. payload varies by event and payment method. created_at is
a UTC RFC3339 envelope timestamp generated for delivery, not a universal
payment timestamp or stable event ID; a retry can have a different value.
Webhook Signature
Use the secret configured for that Webhook endpoint, not the merchant API
secret. Calculate HMAC-SHA256 over the exact raw request body bytes, encode
the digest as lowercase hexadecimal, and prefix it with sha256=. JSON parsing
and re-serialization can change bytes and must not happen before verification.
In Express, capture this route with express.raw({ type: 'application/json' })
before a global express.json() parser. Verify that Buffer first, then
parse its JSON. Dispatch using the verified body's event; do not trust the
convenience X-Webhook-Event header alone. The merchant-API timestamp/nonce
scheme is not the Webhook protocol.
Payment Events and Fulfillment Events
Subscribe only to events enabled for your merchant and deployment. These implementation-defined event names have different meanings:
| Event | Meaning and delivery rule |
|---|---|
fiat.payment.succeeded | A Card payment-level notification; can still be sent while fulfillment is withheld. Record/reconcile it, but do not ship merely because this event or its signature is valid |
order.invoiced | Order-completion notification. Resolve it to the saved purchase and verify current business eligibility before idempotent delivery |
quickpay.checkout.completed | Checkout-completion notification, not a generic payment-success alias. Match it to the saved Checkout Session and associated order before delivery |
When a Card payment is held, the platform suppresses order.invoiced and
Checkout completion while still reporting the payment-level event. Do not
replace that distinction with a generic "any payment success means fulfill"
handler.
Validation and Recovery
- Verify the raw-body signature with the endpoint's secret and reject invalid deliveries. Enforce your receiver's freshness policy without assuming the merchant API's five-minute authentication rule is a Webhook delivery limit.
- Read fields according to the event schema. A payload is not guaranteed to include merchant, amount, Token, and transaction hash on every event.
- Resolve available identifiers to the purchase saved by your backend. Query the authoritative order/payment state when the notification lacks facts needed for authorization or reconciliation; do not silently skip checks.
- Validate terms appropriate to the rail: Crypto chain/Token/amount and any observed transfer, or Card currency/amount and provider payment reference. Card notifications do not have to contain a blockchain transaction hash.
- Process atomically and idempotently using a stable business-operation key.
The envelope's
eventis a name, not a unique delivery ID. Do not usecreated_atas a stable deduplication key or collapse all event types for an order into one already-processed flag. - Return a 2xx response only after durable acceptance. Handle retries and out-of-order notifications without regressing state or delivering twice.
Polling remains a valid recovery path. Polling and webhook delivery can race, so both must converge on the same idempotent fulfillment operation.
Error Semantics
| HTTP status | Meaning |
|---|---|
200 | Request succeeded |
202 | MPP verification is pending_inclusion or pending_finality; not completion. Honor Retry-After and retry verification of the original payment, not the transfer |
400 | Validation or business error |
401 | Invalid/expired merchant authentication, reused nonce (Replay request detected), or an MPP credential/challenge error; inspect the endpoint-specific body |
402 | Payment is required; expected in create and challenge flows |
403 | Authenticated caller is not authorized |
404 | Resource not found |
409 | Resource/business conflict: duplicate Checkout reference, payer/rail conflict, or an already-claimed MPP transaction. Merchant authentication nonce replay is 401, not 409 |
410 | A Checkout operation cannot proceed, for example an expired/cancelled/already-completed session or too little time to start Card payment; a status read may instead return 200 with a terminal state |
429 | Rate limited; honor Retry-After when present |
500 / 503 | Service error or temporary unavailability |
An HTTP status alone is not a payment or fulfillment result. In particular, 202 does not authorize delivery, and 410 does not prove that no funds moved. Preserve the original purchase, session/order IDs, and payment evidence for reconciliation.
Retry only operations documented as safe. An application request ID is not a
universal platform idempotency parameter: ordinary orders reject duplicate
dapp_order_id, and Checkout Sessions reject duplicate non-empty
client_reference_id. Neither create API recovers the original response on
an ordinary retry. Preserve the original request and any known IDs or
transaction hashes, then reconcile an unknown create result before issuing
another payable order/session or initiating another payment. Follow the
MPP recovery rules for
challenge and receipt errors; an expired challenge is not proof of non-payment.
Public Integration Rules
- Use generated QuickPay and Checkout URLs; do not guess a service origin.
- PayKit SDKs may expose
QuickPay*class and component names because QuickPay is the solution being integrated. Keep those code identifiers unchanged. - Keep merchant credentials and signing secrets off the browser and client apps.
- Treat browser callbacks and redirects as interface signals only.
- Fulfill from verified status, authenticated webhooks, or a verified receipt.
- Keep production and test credentials and resources strictly separate.
See SDK Reference, Checkout, and API Payments.