tbltapDeveloperDeveloper Portal

API reference

Developer docs

Request, response, status code, error, webhook, and rate-limit references for hosted menu links and partner integrations.

Public partner reviewUpdated Jul 13, 2026

Authentication

Every partner request authenticates with a bearer access token. Mutating requests additionally require an idempotency key so retried writes never duplicate.

Every request
Authorization: Bearer <partner_access_token>
Tbltap-API-Version: 2026-07-13
Idempotency-Key: partner-generated-stable-key
Accept: application/json

Orders

POST/v1/table-sessions/{session_id}/orders

Create order handoff

Creates a retry-safe order handoff tied to a table session, restaurant service state, and staff review rules.

Partner-scoped write30 requests/minute per app + session

Request schema

external_order_idrequired
stringExample: pos_9b2f

Partner-generated stable order reference used for reconciliation and support.

items[].menu_item_idrequired
stringExample: mi_crispy_rice

Approved menu item identifier from the menu catalog response.

items[].quantityrequired
integerExample: 2

Quantity of the item, must be at least 1.

metadata
object

Optional partner-owned reconciliation metadata. Do not include secrets or payment data.

Errors

POST/v1/table-sessions/{session_id}/orders

Response samples

application/json
200 OKAccepted or idempotently replayed
{
  "object": "order_handoff",
  "id": "ordh_123",
  "table_session_id": "tbs_123",
  "restaurant_id": "rst_123",
  "status": "pending_staff_review",
  "idempotency_status": "created",
  "items_received": 1,
  "created_at": "2026-07-13T16:03:12Z",
  "request_id": "req_order_01j2x9r9k1"
}
400 Bad RequestValidation failed
{
  "error": {
    "code": "invalid_request",
    "message": "items[0].quantity must be at least 1.",
    "type": "invalid_request_error",
    "field": "items[0].quantity"
  },
  "request_id": "req_order_validation",
  "docs_url": "https://tblt.app/docs/developers#orders-create"
}
409 ConflictSession cannot accept the mutation
{
  "error": {
    "code": "table_session_closed",
    "message": "This table session is closed and cannot accept new orders.",
    "type": "conflict_error"
  },
  "request_id": "req_order_conflict",
  "docs_url": "https://tblt.app/docs/developers#orders-create"
}
GET/v1/table-sessions/{session_id}/orders/{order_id}

Get order handoff

Reads current staff-review status, item-level state, and any rejection reason for a submitted order handoff.

Partner-scoped read60 requests/minute per app + session

Request schema

order_idrequired
stringExample: ordh_123

Order handoff identifier returned from the create call.

Errors

GET/v1/table-sessions/{session_id}/orders/{order_id}

Response samples

application/json
200 OKOrder handoff returned
{
  "object": "order_handoff",
  "id": "ordh_123",
  "status": "accepted",
  "items": [
    { "menu_item_id": "mi_crispy_rice", "quantity": 2, "state": "fired" }
  ],
  "request_id": "req_order_get_01j2x9rd2m"
}
401 UnauthorizedMissing or invalid credential
{
  "error": {
    "code": "invalid_api_key",
    "message": "Use a valid partner access token.",
    "type": "authentication_error"
  },
  "request_id": "req_unauthorized",
  "docs_url": "https://tblt.app/docs/developers#authentication"
}
404 Not FoundOrder handoff is outside scope
{
  "error": {
    "code": "order_handoff_not_found",
    "message": "No order handoff was found for this id and credential scope.",
    "type": "invalid_request_error"
  },
  "request_id": "req_order_get_missing",
  "docs_url": "https://tblt.app/docs/developers#orders-get"
}
PUT/v1/table-sessions/{session_id}/orders/{order_id}/cancel

Cancel order handoff

Cancels a pending order handoff before staff acceptance. Fired or accepted items cannot be canceled through this route.

Partner-scoped write30 requests/minute per app + session

Request schema

order_idrequired
string

Order handoff identifier to cancel.

reason
string

Optional partner-supplied cancellation reason for support traceability.

Errors

PUT/v1/table-sessions/{session_id}/orders/{order_id}/cancel

Response samples

application/json
200 OKOrder handoff canceled
{
  "object": "order_handoff",
  "id": "ordh_123",
  "status": "canceled",
  "request_id": "req_order_cancel_01j2x9re9v"
}
409 ConflictOrder has already been accepted
{
  "error": {
    "code": "order_already_accepted",
    "message": "This order handoff has already been accepted by staff and cannot be canceled.",
    "type": "conflict_error"
  },
  "request_id": "req_order_cancel_conflict",
  "docs_url": "https://tblt.app/docs/developers#orders-cancel"
}
404 Not FoundOrder handoff is outside scope
{
  "error": {
    "code": "order_handoff_not_found",
    "message": "No order handoff was found for this id and credential scope.",
    "type": "invalid_request_error"
  },
  "request_id": "req_order_cancel_missing",
  "docs_url": "https://tblt.app/docs/developers#orders-cancel"
}

Payments

GET/v1/table-sessions/{session_id}/bill

Get bill state

Reads bill readiness, split state, and payment status without treating client timeouts as payment truth.

Partner-scoped read60 requests/minute per app + session

Request schema

table_session_idrequired
string

Table session being read. The credential must be scoped to the restaurant and session.

bill_ready
boolean

Whether staff/server state says the bill can be presented for payment.

payment_status
enumExample: open

Current payment status: open, partially_paid, or paid.

Errors

GET/v1/table-sessions/{session_id}/bill

Response samples

application/json
200 OKCurrent bill snapshot
{
  "object": "table_bill",
  "table_session_id": "tbs_123",
  "restaurant_id": "rst_123",
  "bill_ready": true,
  "currency": "USD",
  "subtotal_minor": 5200,
  "tax_minor": 468,
  "service_charge_minor": 260,
  "tip_minor": 1040,
  "total_minor": 6968,
  "amount_paid_minor": 0,
  "payment_status": "open",
  "split_mode": "none",
  "updated_at": "2026-07-13T16:05:00Z",
  "request_id": "req_bill_01j2x9r9pz"
}
403 ForbiddenCredential lacks required scope
{
  "error": {
    "code": "scope_denied",
    "message": "The credential does not include table_sessions.bill.read.",
    "type": "authorization_error",
    "required_scope": "table_sessions.bill.read"
  },
  "request_id": "req_bill_forbidden",
  "docs_url": "https://tblt.app/docs/developers#payments-bill"
}
503 Service UnavailablePayment state is being reconciled
{
  "error": {
    "code": "provider_state_unavailable",
    "message": "Payment state is being reconciled. Retry with backoff or wait for webhook confirmation.",
    "type": "api_error",
    "retry_after_seconds": 30
  },
  "request_id": "req_bill_provider_unavailable",
  "docs_url": "https://tblt.app/docs/developers#payments-bill"
}
POST/v1/table-sessions/{session_id}/payment-intents

Create payment intent

Creates a payment intent for a full or split bill amount ahead of guest checkout.

Partner-scoped write30 requests/minute per app + session

Request schema

amount_minorrequired
integer

Amount to collect, in minor currency units. Must not exceed the remaining bill total.

split_group_id
string

Optional identifier grouping guests paying the same bill together.

Errors

POST/v1/table-sessions/{session_id}/payment-intents

Response samples

application/json
200 OKPayment intent created
{
  "object": "payment_intent",
  "id": "pi_123",
  "amount_minor": 3484,
  "status": "requires_confirmation",
  "request_id": "req_pi_01j2x9rf3x"
}
400 Bad RequestAmount exceeds payable state
{
  "error": {
    "code": "invalid_request",
    "message": "amount_minor exceeds the remaining bill total.",
    "type": "invalid_request_error",
    "field": "amount_minor"
  },
  "request_id": "req_pi_validation",
  "docs_url": "https://tblt.app/docs/developers#payments-intent"
}
409 ConflictBill is locked
{
  "error": {
    "code": "bill_locked",
    "message": "This bill is locked while another payment is in progress.",
    "type": "conflict_error"
  },
  "request_id": "req_pi_conflict",
  "docs_url": "https://tblt.app/docs/developers#payments-intent"
}
POST/v1/payment-intents/{payment_intent_id}/confirm

Confirm payment

Confirms a payment intent once the provider has authorized funds. tbltap reconciles authoritative state before marking a bill paid.

Partner-scoped write30 requests/minute per app + session

Request schema

payment_intent_idrequired
string

Payment intent identifier returned from the create call.

provider_charge_idrequired
string

Charge or authorization identifier from the payment provider.

Errors

POST/v1/payment-intents/{payment_intent_id}/confirm

Response samples

application/json
200 OKPayment confirmed
{
  "object": "payment_intent",
  "id": "pi_123",
  "status": "succeeded",
  "confirmed_at": "2026-07-13T16:11:00Z",
  "request_id": "req_pi_confirm_01j2x9rga1"
}
409 ConflictPayment intent is finalized
{
  "error": {
    "code": "payment_intent_finalized",
    "message": "This payment intent has already been confirmed or canceled.",
    "type": "conflict_error"
  },
  "request_id": "req_pi_confirm_conflict",
  "docs_url": "https://tblt.app/docs/developers#payments-confirm"
}
503 Service UnavailableProvider confirmation is pending
{
  "error": {
    "code": "provider_state_unavailable",
    "message": "Provider confirmation is pending. Retry with backoff or wait for webhook confirmation.",
    "type": "api_error",
    "retry_after_seconds": 20
  },
  "request_id": "req_pi_confirm_unavailable",
  "docs_url": "https://tblt.app/docs/developers#payments-confirm"
}

Webhooks

POST/v1/webhook-endpoints

Create webhook endpoint

Registers a signed event destination for approved order, payment, table, POS, or delivery events.

Partner-scoped write10 requests/minute per app

Request schema

urlrequired
https urlExample: https://partner.example.com/tbltap/webhooks

Partner destination that receives signed tbltap event payloads.

event_types[]required
array<string>Example: payment.succeeded

Approved event types for this partner credential and environment.

signing_secret
string

Returned only at creation time. Store it securely and never send it to support.

Errors

POST/v1/webhook-endpoints

Response samples

application/json
200 OKWebhook endpoint registered
{
  "object": "webhook_endpoint",
  "id": "we_123",
  "url": "https://partner.example.com/tbltap/webhooks",
  "enabled": true,
  "event_types": [
    "order_handoff.created",
    "table_session.bill_ready",
    "payment.succeeded"
  ],
  "signing_secret": "whsec_test_visible_once",
  "created_at": "2026-07-13T16:08:00Z",
  "request_id": "req_webhook_01j2x9ra2f"
}
409 ConflictDuplicate endpoint
{
  "error": {
    "code": "webhook_endpoint_exists",
    "message": "A webhook endpoint already exists for this URL and partner app.",
    "type": "conflict_error"
  },
  "request_id": "req_webhook_duplicate",
  "docs_url": "https://tblt.app/docs/developers#webhooks-create"
}
500 Internal Server ErrorUnexpected tbltap error
{
  "error": {
    "code": "internal_error",
    "message": "The request could not be completed. Retry safely with the same idempotency key.",
    "type": "api_error"
  },
  "request_id": "req_webhook_internal",
  "docs_url": "https://tblt.app/docs/developers#errors"
}
GET/v1/webhook-endpoints

List webhook endpoints

Lists all webhook endpoints registered for the partner app, including enabled state and subscribed event types.

Partner-scoped read60 requests/minute per app

Request schema

limit
integer

Maximum number of endpoints to return, default 20, max 100.

Errors

GET/v1/webhook-endpoints

Response samples

application/json
200 OKWebhook endpoint list returned
{
  "object": "list",
  "data": [
    {
      "id": "we_123",
      "url": "https://partner.example.com/tbltap/webhooks",
      "enabled": true
    }
  ],
  "request_id": "req_webhook_list_01j2x9rh6e"
}
401 UnauthorizedMissing or invalid credential
{
  "error": {
    "code": "invalid_api_key",
    "message": "Use a valid partner access token.",
    "type": "authentication_error"
  },
  "request_id": "req_unauthorized",
  "docs_url": "https://tblt.app/docs/developers#authentication"
}
PUT/v1/webhook-endpoints/{webhook_endpoint_id}/disable

Disable webhook endpoint

Disables a webhook endpoint without deleting its history. Disabled endpoints stop receiving new events immediately.

Partner-scoped write10 requests/minute per app

Request schema

webhook_endpoint_idrequired
string

Webhook endpoint identifier to disable.

Errors

PUT/v1/webhook-endpoints/{webhook_endpoint_id}/disable

Response samples

application/json
200 OKWebhook endpoint disabled
{
  "object": "webhook_endpoint",
  "id": "we_123",
  "enabled": false,
  "request_id": "req_webhook_disable_01j2x9ri9r"
}
404 Not FoundWebhook endpoint is outside scope
{
  "error": {
    "code": "webhook_endpoint_not_found",
    "message": "No webhook endpoint was found for this id and credential scope.",
    "type": "invalid_request_error"
  },
  "request_id": "req_webhook_disable_missing",
  "docs_url": "https://tblt.app/docs/developers#webhooks-disable"
}

Status codes

200 OKRequest succeeded. Mutating endpoints return the same object on idempotent replay.
400 Bad RequestJSON, required field, enum, amount, URL, or schema validation failed.
401 UnauthorizedMissing, expired, or invalid credential.
403 ForbiddenCredential is valid but lacks restaurant, environment, or scope access.
404 Not FoundResource does not exist or is outside the credential scope.
409 ConflictState conflict such as closed table session, duplicate endpoint, locked bill, or replay mismatch.
429 Too Many RequestsRate limit exceeded. Use Retry-After and X-RateLimit-Reset.
500 Internal Server ErrorUnexpected tbltap error. Retry only when the operation is idempotent.
503 Service UnavailableDependency or provider state is unavailable. Back off or wait for webhook reconciliation.

Rate limits

Public hosted linksNo partner API quota

Browser traffic is protected by edge caching, abuse controls, and route-specific safeguards.

Sandbox partner API60 reads/minute, 30 writes/minute

Counts are per partner app plus restaurant or table session. Lower limits can apply to webhook-adjacent routes.

Webhook management10 writes/minute

Registering or updating webhook endpoints is limited separately from read/write traffic to reduce accidental fan-out.

Response headers
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1783987260
Retry-After: 42
Content-Type: application/json

Error envelope

Clients should branch on error.code, not prose. Every failure includes a support-traceable request ID.

Shared 4xx / 5xx shape
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests for this restaurant and partner app.",
    "type": "rate_limit_error",
    "retry_after_seconds": 42
  },
  "request_id": "req_01j2x9r8d7a9",
  "docs_url": "https://tblt.app/docs/developers#errors"
}

LLM extraction

Read the machine-readable JSON script tag first, then use the visible operation sections for examples.

Extraction selector
document.querySelector('#tbltap-developer-api-spec')?.textContent

Safety rules

  • Never send API keys, webhook secrets, cookies, card numbers, one-time codes, or private staff credentials to support.
  • Do not store more customer, order, payment, or table data than the integration needs.
  • Do not scrape private owner, staff, or payment pages as an API substitute.
  • Use tbltap-approved static media URLs for public menu media. Do not emit signed, original-only, third-party, or Supabase render URLs.
  • Do not claim order, delivery, or payment success until the authoritative tbltap and provider states agree.

Next steps

  1. Describe the restaurant use case, data needed, write operations, and webhook events.
  2. Request access with a technical contact, company name, callback URL, and desired sandbox timeline.
  3. Build against sandbox credentials and document retry, idempotency, logging, and rollback behavior.
  4. Complete production certification before live restaurant traffic.
Developer docs | tbltap Docs