API Reference · Geat.io

Geat.io v1 API

Reference for the public /v1 HTTP surface. Every field, header, status code and error code on these pages is copied from the code; each section carries an HTML comment naming the source file and lines.

Non-custodial: the API never moves funds. Payment addresses are derived from the merchant's own xpub, payments land directly in the merchant's wallet, and the API only detects and bookkeeps.

Pages

PageContents
InvoicesHosted-invoice endpoints (/v1/invoices)
AddressesBare address requests + rates (/v1/addresses)
Payer endpointsPayer-page endpoints (/v1/pay) - no API key
WebhooksThe four merchant webhook events, signing, retries
Error codesEvery error code the v1 surface emits

Quickstart

The whole hosted-invoice integration is five steps.

1. Create the invoice with your API key. The merchant profile_uuid and the three URLs come from your own system.

curl -X POST https://api.geat.io/v1/invoices \
  -H "X-API-Key: $GEATIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_uuid": "2c9a1f44-5d3b-4f0e-9a21-7c6b8e2d4f10",
    "invoice_id": "ORDER-1042",
    "price": 49.00,
    "currency": "USD",
    "success_url": "https://shop.example.com/thanks",
    "failure_url": "https://shop.example.com/oops",
    "callback_url": "https://shop.example.com/hooks/geatio"
  }'

2. Send the payer to data.invoice_url from that response. The hosted page handles coin choice, the address, the QR code and the countdown. Nothing else on your side is needed until the payment resolves. (Pass "redirect": true and the create call answers 302 to that URL directly, if you would rather redirect than read the body.)

3. Receive the webhook at your callback_url: invoice.completed when the payment confirms, invoice.expired when the window closes.

4. Verify the signature before trusting the body - HMAC-SHA256 over {X-Timestamp}.{raw body}, compared in constant time against X-Signature. See webhooks.md.

5. Confirm against the API before releasing goods: GET /v1/invoices/{invoice_uuid}. The webhook is a nudge; this endpoint is the authority.

Bare receive addresses, with no hosted page, follow the same shape using POST /v1/addresses and the address.paid / address.payment webhooks

Two things integrators assume that are not true here

  • ·There are no list endpoints. v1 has no "list my invoices" or "list my addresses" collection route - every read is by UUID. Store the uuid we return against your own order record at creation time; it is the only handle you get. Browsing and exporting live in the dashboard.
  • ·Only POST /v1/addresses is idempotent. It honours an optional Idempotency-Key header. POST /v1/invoices does not: there is no idempotency header and invoice_id is not unique, so retrying a create that already succeeded makes a second invoice. Treat a timed-out create as unknown and reconcile against your own records rather than blindly retrying.

Base URL

https://api.geat.io

This is the live production endpoint. Development installs serve the same paths from their own origin.

Authentication

Merchant endpoints authenticate with an API key sent in the X-API-Key request header:

X-API-Key: <your api key>

Keys are issued per merchant account in the dashboard. They are stored hashed, matched against non-revoked rows only, and their last_used_at is refreshed on every accepted call.

The authenticator rejects, in this order:

ConditionStatusCode
Missing, unknown, or revoked key401UNAUTHORIZED
Owner account not active403ACCOUNT_INACTIVE
Owner email not verified403EMAIL_NOT_VERIFIED
Owner account suspended403ACCOUNT_SUSPENDED

The payer endpoints in Payer endpoints take no API key - they are the public invoice page's own backend and are documented here only for completeness.

Response envelope

Success:

{
  "success": true,
  "data": { },
  "message": "..."
}

data is omitted when the endpoint returns nothing, and message is omitted when there is no human-readable note. Both can be present.

Error:

{
  "success": false,
  "error": "Human-readable message.",
  "code": "MACHINE_CODE"
}

code is omitted only if the handler raised the error without one; every v1 error documented in Error codes carries one. Branch on code, never on error - the prose may be reworded.

Rate limits

Merchant /v1 endpoints (invoices + addresses) use a per-request dynamic limit:

CallerLimit
Authenticated (valid API key)10 per second; 100 per minute; 10000 per day
Unauthenticated / invalid key5 per minute

The bucket key is the authenticated key's UUID (apikey:<uuid>), or the caller's IP (ip:<address>) when no valid key was presented. Callers whose upstream address cannot be parsed as an IP share one ip:unknown bucket.

Other surfaces, for context:

  • ·Payer /v1/pay endpoints: blanket 5 per second, applied both to the blueprint and per route. GET /v1/pay/{invoice_uuid}/review carries its own 10 per second.
  • ·Dashboard (cookie-authenticated, non-/v1) blueprints: blanket 10 per second floor, with tighter per-route limits where decorated.
  • ·Deliberately unlimited: the watch-service webhook receiver (HMAC-signed machine traffic that must never be throttled - a 429 there drops a payment credit) and the bare GET /health liveness probe (a 429 on a probe reads as "down"). GET /health/detail is not exempt and keeps its own 30 per minute.

A throttled request answers 429. No custom 429 handler is registered, so that response is the rate limiter's default and does not use the success/error envelope above; treat the status code as the contract.

Money and amounts

  • ·Fiat prices are strings with 2 decimal places.
  • ·Crypto amounts are fixed-point decimal strings, never scientific notation. Invoice-page amounts are 8 decimal places truncated (never rounded up); ledger and webhook amounts are 18 decimal places.
  • ·fee_amount is always 0 on both the API and webhook surfaces. The product charges prepaid credits (1 credit per payment attempt), not a percentage of the payment, so no fee is ever deducted from a merchant's funds. The field exists for integrators who already parse it.

Detection depth

Every derived address is registered with the watch service, which keeps it in a hot tier (live detection) for a default of 24 hours, then demotes it to a cold tail that is still reconciled for a default of 120 days. A payment that lands late is therefore still detected and credited inside the cold window; after it, nothing is lost on-chain (the funds are already in the merchant's own wallet) but the bookkeeping entry has to be made by hand.