Docs
Documentation

Commerce API

Connect your own shop, booking or admissions system to the Sales agent.

On this page: What it is · Who does what · One order, call by call · What you build · Your options · Discovery profile · Operations · What every call carries · Signatures · Commit, quote and lookup · Examples · Status events · Your payment gateway · Errors · Connect it · Go-live checklist · Conventions

What it is

The Commerce API lets the AI Sales Agent work with your own shop, booking or admissions system: get a quote, and create the order, booking or application in your system the moment the customer confirms (the Created in our system completion mode). Your system stays the system of record: its prices, its stock, its order numbers and its payment gateway.

Unlike Central AI’s other APIs, your system implements this one and Central AI calls it. Contract version: 1.3. Your system may declare any 1.x.

This page is for the developers of that system, for example the team behind a course platform that already sells with SSLCommerz or Stripe. A shop owner who only wants the agent to take orders does not need it: Handled in Central AI needs no code at all.

Start from working code. Central AI provides reference implementations of this contract in Node.js, plain PHP and a Laravel package, with the JSON schemas and conformance checks. Signing, verification and idempotency are done; you connect their few shop methods to your database. Ask your Central AI contact for the package.

Who does what

The Sales Agent (Central AI) does

  • Talks to the customer on your website, Messenger, Instagram and WhatsApp.
  • Finds your offerings, shows them as cards and builds the basket.
  • Asks your system for the price (quote.calculate) and shows that total, never its own.
  • Collects the details you ask for and the payment method your quote lists.
  • Shows the whole request; only the customer confirms it, with a button or a code.
  • Sends exactly one order (commit.create) and, if the answer is lost, finds it (commit.lookup) instead of sending it twice.
  • Shows your payment page as Pay now, and tells the customer every status you send.

Your system does

  • Publishes one discovery document: where your endpoints are, and your public signing key.
  • Prices the basket with its own rules (quote.calculate) and holds that price until it expires.
  • Creates the order once per Idempotency-Key and returns its reference (commit.create).
  • Finds an order by that key (commit.lookup).
  • Takes the payment in its own gateway and returns the payment page (continue_url).
  • Posts a signed status event when the order is accepted, paid, fulfilled or cancelled.
  • Signs every response and event, and verifies every call from Central AI.

Your offerings reach Central AI separately. In this release the agent searches the offerings you keep in Central AI, sent with the offerings feed of the Knowledge Sync API, a CSV file or the offerings screen. Give each one the id it has in your system: that id is the external_ref every quote and order line carries, so your system knows which item is meant. catalog.search, catalog.get, availability.check and commit.get are part of the contract and listed in the connection test report, but Central AI does not call them yet.

One order, call by call

  1. The customer asks in the chat (“Do you have a web development course?”). The agent searches your offerings in Central AI and shows cards. No call to your system.
  2. The customer chooses and asks for the total. Central AI calls quote.calculate with the basket; your answer is the price, fees, delivery, disclosures and the payment methods you accept. It is what the customer sees.
  3. The agent collects the details you require (name, phone, address, your own fields) and the payment method, then shows the whole request with a code. No call to your system.
  4. The customer confirms. Central AI calls commit.create once, with an Idempotency-Key, your quote’s reference and version, the exact amount, the customer and attribution.operation_ref. You create the order and answer with its reference and statuses, plus the continue_url of your payment page if the customer pays online.
  5. If your answer is lost (timeout, 5xx), Central AI calls commit.lookup with the same key before anything else. It never creates a second order.
  6. The customer pays on your gateway through the Pay now button.
  7. You post status events (accepted, paid, fulfilled, cancelled). Central AI verifies each one, updates the order and tells the customer in the chat.

What you build

PieceNeeded forWithout it
Discovery document and your signing keyEverythingThe connection cannot be published or tested.
quote.calculateEvery order: the customer only ever confirms your totalThe agent cannot propose an order in your system.
commit.create and commit.lookup Creating the order, safelyThe connection test fails: a lost answer could become a second order.
Status eventsAccepted, paid, fulfilled and cancelled reaching the customer Orders stay at the status your commit.create answer gave.
continue_url in the commit answerCustomers paying online in your gateway The customer is not given a payment page in the chat.
Offerings feed (Knowledge Sync API) with your idsThe agent finding and showing your items The agent has nothing to offer.

Your options

IntegrationStatus
Commerce API (your own system)available
Handled in Central AI (no connection needed)available
WooCommercecoming soon
Shopifycoming soon

WooCommerce and Shopify are coming soon. Until then a WooCommerce or Shopify shop can take orders in the chat (Handled in Central AI), hand the basket to its own checkout and follow the order live (Live checkout on your site), send customers to its own pages (On our page), or connect through the Commerce API.

Discovery profile

Your system publishes a discovery document: a JSON file that says which contract version it speaks, which operations it supports and at which paths, how long it keeps idempotency keys, the offering kinds and currencies it handles, and its public signing keys. The reference implementations serve it at /.well-known/central-ai-commerce under your base address, for example https://yourshop.com/.well-known/central-ai-commerce. You give Central AI that address as the profile address.

  • Every endpoint it names must be https://, on the same host as your base address.
  • It must keep idempotency keys for at least 7 days.
  • Its keys hold public keys only.
  • Central AI fetches it without following redirects.
Example discovery document
{
  "contract": "central-ai-commerce",
  "version": "1.2",
  "system": {
    "name": "DIU Admissions Portal",
    "vendor": "DIU ICT Division",
    "software_version": "3.2.0",
    "timezone": "Asia/Dhaka"
  },
  "capabilities": [
    {
      "name": "catalog.search",
      "version": "1.0"
    },
    {
      "name": "catalog.get",
      "version": "1.0"
    },
    {
      "name": "availability.check",
      "version": "1.0"
    },
    {
      "name": "quote.calculate",
      "version": "1.0",
      "quote_modes": [
        "estimate",
        "binding"
      ]
    },
    {
      "name": "commit.create",
      "version": "1.0"
    },
    {
      "name": "commit.lookup",
      "version": "1.0"
    },
    {
      "name": "commit.get",
      "version": "1.0"
    },
    {
      "name": "events",
      "version": "1.0"
    }
  ],
  "endpoints": {
    "base_url": "https://admissions.diu.example/api/central-ai",
    "catalog_search": {
      "method": "POST",
      "url": "https://admissions.diu.example/api/central-ai/catalog/search"
    },
    "catalog_get": {
      "method": "GET",
      "url": "https://admissions.diu.example/api/central-ai/catalog/{offering_ref}"
    },
    "availability_check": {
      "method": "POST",
      "url": "https://admissions.diu.example/api/central-ai/availability/check"
    },
    "quote_calculate": {
      "method": "POST",
      "url": "https://admissions.diu.example/api/central-ai/quotes"
    },
    "commit_create": {
      "method": "POST",
      "url": "https://admissions.diu.example/api/central-ai/commitments"
    },
    "commit_lookup": {
      "method": "POST",
      "url": "https://admissions.diu.example/api/central-ai/commitments/lookup"
    },
    "commit_get": {
      "method": "GET",
      "url": "https://admissions.diu.example/api/central-ai/commitments/{reference}"
    }
  },
  "events": {
    "supported": true,
    "signature_algorithms": [
      "ecdsa-p256-sha256"
    ],
    "event_types": [
      "commitment.created",
      "commitment.accepted",
      "commitment.rejected",
      "commitment.canceled",
      "commitment.updated",
      "payment.updated"
    ],
    "delivery_url_registration": "configured_by_tenant"
  },
  "idempotency": {
    "retention_days": 30
  },
  "offering_kinds": [
    "course_intake"
  ],
  "currencies": [
    "BDT"
  ],
  "checkout_modes": [
    "connector_write"
  ],
  "guest_checkout": true,
  "updated_at": "2026-09-01T06:00:00Z",
  "keys": {
    "keys": [
      {
        "kty": "EC",
        "crv": "P-256",
        "x": "Jj1vO3UwxbB1GKTPEW7huAKKG_xLJj1SEYccYc4nGTY",
        "y": "ekSfe2231HejJvRPACbLoVkXjsNrnjO-gzaIlfyULGY",
        "kid": "diu-2026-09",
        "use": "sig",
        "alg": "ES256"
      }
    ]
  }
}

Operations

The operations the connection test checks for: catalog.search, catalog.get, availability.check, quote.calculate, commit.create, commit.lookup, commit.get, events. Created in our system cannot work without commit.create and commit.lookup.

OperationMethodReference pathWhat
catalog.searchPOST/catalog/searchSearch offerings.
catalog.getGET/catalog/{offering_ref}One offering, re-checked live.
availability.checkPOST/availability/checkStock, seats or slots per line.
quote.calculatePOST/quotesTotals for a selection. Required before any binding commit.
commit.createPOST/commitmentsCreate the order, booking or application. Idempotency-Key required.
commit.lookupPOST/commitments/lookupFind a commitment by the original Idempotency-Key.
commit.getGET/commitments/{reference}Current statuses by your reference.

The paths are the reference implementations’; yours are whatever your discovery document declares. events is the other direction: your system sends status events to Central AI.

What every call from Central AI carries

  • Accept: application/json
  • Authorization: Bearer <the key your system issued for Central AI>
  • Idempotency-Key: on every POST; only commit.create stores and replays it
  • Request-Id: one per attempt; echo it in errors as correlation_id
  • Central-AI-Contract-Version: 1.3
  • Content-Type: application/json, with a body
  • UCP-Agent: names Central AI’s platform profile
  • Content-Digest: SHA-256 of the body, with a body
  • Signature-Input: the request signature (below)
  • Signature: the request signature (below)

Signatures

Every call in both directions is signed with RFC 9421 HTTP Message Signatures, algorithm ecdsa-p256-sha256, with an RFC 9530 Content-Digest of the body.

  • Verify Central AI’s requests with the public key in keys[] of our platform profile at http://centralai.daffodil-bd.com/.well-known/ucp.
  • Sign every response and every status event with your key, published in your discovery document. Central AI pins your keys when you publish the connection, and believes nothing that does not verify.
  • A signature older than 300 seconds, or more than 60 seconds in the future, is refused.

Signing is where most first attempts fail. The reference implementations sign and verify for you, and ship the contract’s signature test vectors: requests, responses and events Central AI accepts, and tampered ones it refuses. Run them before you connect.

Commit, quote and lookup

  • Quote (quote.calculate) comes before any binding commit. For an order created in your system, the totals the customer sees are your quote’s, checked by Central AI.
  • Commit (commit.create) carries an Idempotency-Key. The same key with the same body replays your original answer (Idempotent-Replayed: true); the same key with a different body is 409 idempotency_conflict; a key still running is 409 idempotency_in_flight with Retry-After. A changed price is the quote_mismatch outcome (HTTP 200, no commitment, the current quote), never a silent re-price.
  • Lookup (commit.lookup) finds a commitment by the original Idempotency-Key, so an answer lost on the network is recovered without creating a second order. Central AI never retries with a new key.
  • Test order. The connection test sends one clearly marked test order ("test": true) and looks it up. Never fulfil it.

Examples

These are the contract’s own examples, validated against its JSON schemas, from a sample admissions portal whose applicants pay by bank deposit. The payment gateway section has a card-and-bKash example.

quote.calculate: the request Central AI sends
{
  "selection": {
    "ref": "sel_diu_4Hn8",
    "revision": 2,
    "fingerprint": "c029b155b21205f7f8917b1d8cc2ce86cc654cf45cfb0145b8abd5f507479faf",
    "lines": [
      {
        "line_ref": "line_1",
        "offering_ref": "off_diu_pdds",
        "external_ref": "PDDS",
        "title": "Professional Diploma in Data Science",
        "quantity": 1,
        "cohort_ref": "coh_diu_pdds_2027_01",
        "unit_price": {
          "amount": "45000.00",
          "currency": "BDT"
        }
      }
    ]
  },
  "currency": "BDT",
  "buyer": {
    "payment_method_code": "bank_deposit"
  },
  "requested_mode": "binding"
}
quote.calculate: your answer
{
  "quote": {
    "schema_version": "1.0",
    "quote_ref": "quote_diu_88213",
    "version": 1,
    "selection_ref": "sel_diu_4Hn8",
    "selection_revision": 2,
    "selection_fingerprint": "c029b155b21205f7f8917b1d8cc2ce86cc654cf45cfb0145b8abd5f507479faf",
    "currency": "BDT",
    "mode": "binding",
    "items": [
      {
        "line_ref": "line_1",
        "offering_ref": "off_diu_pdds",
        "external_ref": "PDDS",
        "title": "Professional Diploma in Data Science",
        "variant_label": "January 2027 intake",
        "quantity": 1,
        "unit_price": {
          "amount": "45000.00",
          "currency": "BDT"
        },
        "amount": {
          "amount": "45000.00",
          "currency": "BDT"
        }
      }
    ],
    "fees": [],
    "discounts": [],
    "subtotal": {
      "amount": "45000.00",
      "currency": "BDT"
    },
    "total": {
      "amount": "45000.00",
      "currency": "BDT"
    },
    "payment_methods": [
      {
        "code": "bank_deposit",
        "label": "Bank deposit to the DIU account",
        "collected_offline": true
      }
    ],
    "expires_at": "2026-09-15T11:35:02Z",
    "disclosures": [
      "Tuition of ৳45,000.00 is payable by bank deposit within 14 days of acceptance.",
      "DIU holds this amount until the quote expires; submitting after that needs a new quote."
    ],
    "calculated_at": "2026-09-15T11:05:02Z"
  }
}
commit.create: the request Central AI sends
{
  "kind": "enrollment",
  "quote_ref": "quote_diu_88213",
  "quote_version": 1,
  "amount": {
    "amount": "45000.00",
    "currency": "BDT"
  },
  "selection_fingerprint": "c029b155b21205f7f8917b1d8cc2ce86cc654cf45cfb0145b8abd5f507479faf",
  "terms_hash": "43815c8cb05660a58b85530a1301ad220d2a51bc1ac0c08af9c26fdb007c6b4c",
  "selection": {
    "ref": "sel_diu_4Hn8",
    "revision": 2,
    "lines": [
      {
        "line_ref": "line_1",
        "offering_ref": "off_diu_pdds",
        "external_ref": "PDDS",
        "title": "Professional Diploma in Data Science",
        "quantity": 1,
        "cohort_ref": "coh_diu_pdds_2027_01",
        "unit_price": {
          "amount": "45000.00",
          "currency": "BDT"
        }
      }
    ]
  },
  "customer": {
    "name": "Nusrat Jahan",
    "phone": "+8801900000031",
    "email": "[email protected]",
    "fields": {
      "highest_degree": "BSc in Computer Science",
      "graduation_year": "2024"
    }
  },
  "payment_method_code": "bank_deposit",
  "completion": {
    "mode": "connector_write"
  },
  "channel": "web_widget",
  "attribution": {
    "operation_ref": "op_01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
    "commitment_ref": "cmt_diu_ad311"
  }
}
commit.create: your answer when the order is created (201)
{
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "replayed": false,
  "commitment": {
    "reference": "AD-311",
    "kind": "enrollment",
    "statuses": {
      "acceptance": "pending",
      "payment": "unpaid",
      "fulfilment": "not_started"
    },
    "terms_version": 1,
    "amounts": {
      "total": {
        "amount": "45000.00",
        "currency": "BDT"
      },
      "paid": {
        "amount": "0.00",
        "currency": "BDT"
      },
      "due": {
        "amount": "45000.00",
        "currency": "BDT"
      }
    },
    "payment": {
      "code": "bank_deposit",
      "label": "Bank deposit to the DIU account",
      "collected_offline": true
    },
    "provider_status": {
      "application_status": "submitted",
      "fee_status": "unpaid"
    },
    "created_at": "2026-09-15T11:07:31Z",
    "updated_at": "2026-09-15T11:07:31Z",
    "note": "Upload your graduation certificate on the admissions portal before 15 December 2026."
  },
  "attribution": {
    "operation_ref": "op_01J8ZR7D5W3N8P1K6T2Y4M9Q0A"
  }
}
commit.create: your answer when the price changed (quote_mismatch, 200)
{
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "replayed": false,
  "quote": {
    "schema_version": "1.0",
    "quote_ref": "quote_diu_88213",
    "version": 2,
    "selection_ref": "sel_diu_4Hn8",
    "selection_revision": 2,
    "selection_fingerprint": "c029b155b21205f7f8917b1d8cc2ce86cc654cf45cfb0145b8abd5f507479faf",
    "currency": "BDT",
    "mode": "binding",
    "items": [
      {
        "line_ref": "line_1",
        "offering_ref": "off_diu_pdds",
        "external_ref": "PDDS",
        "title": "Professional Diploma in Data Science",
        "variant_label": "January 2027 intake",
        "quantity": 1,
        "unit_price": {
          "amount": "47000.00",
          "currency": "BDT"
        },
        "amount": {
          "amount": "47000.00",
          "currency": "BDT"
        }
      }
    ],
    "fees": [],
    "discounts": [],
    "subtotal": {
      "amount": "47000.00",
      "currency": "BDT"
    },
    "total": {
      "amount": "47000.00",
      "currency": "BDT"
    },
    "payment_methods": [
      {
        "code": "bank_deposit",
        "label": "Bank deposit to the DIU account",
        "collected_offline": true
      }
    ],
    "expires_at": "2026-09-15T11:37:40Z",
    "disclosures": [
      "Tuition for the January 2027 intake is now ৳47,000.00, payable by bank deposit within 14 days of acceptance.",
      "DIU holds this amount until the quote expires; submitting after that needs a new quote."
    ],
    "calculated_at": "2026-09-15T11:07:40Z"
  },
  "messages": [
    {
      "type": "error",
      "code": "quote_mismatch",
      "severity": "recoverable",
      "content": "The tuition for this intake changed after you reviewed it, so nothing was submitted. Please review the updated amount.",
      "path": "$.amount",
      "mismatch": [
        "amount",
        "quote_version"
      ]
    }
  ]
}
commit.lookup: the request
{
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "operation_ref": "op_01J8ZR7D5W3N8P1K6T2Y4M9Q0A"
}
commit.lookup: found
{
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "found": true,
  "searched_at": "2026-09-15T11:08:05Z",
  "commitment": {
    "reference": "AD-311",
    "kind": "enrollment",
    "statuses": {
      "acceptance": "pending",
      "payment": "unpaid",
      "fulfilment": "not_started"
    },
    "terms_version": 1,
    "amounts": {
      "total": {
        "amount": "45000.00",
        "currency": "BDT"
      },
      "paid": {
        "amount": "0.00",
        "currency": "BDT"
      },
      "due": {
        "amount": "45000.00",
        "currency": "BDT"
      }
    },
    "provider_status": {
      "application_status": "submitted",
      "fee_status": "unpaid"
    },
    "created_at": "2026-09-15T11:07:31Z",
    "updated_at": "2026-09-15T11:07:31Z"
  },
  "attribution": {
    "operation_ref": "op_01J8ZR7D5W3N8P1K6T2Y4M9Q0A"
  },
  "retention_days": 30
}
commit.lookup: never received (Central AI may send it again under the same key)
{
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "found": false,
  "searched_at": "2026-09-15T11:08:05Z",
  "retention_expired": false,
  "retention_days": 30
}
catalog.search and availability.check (in the contract, not called yet)
{
  "query": "data science diploma",
  "kind": "course_intake",
  "available_only": true,
  "from_date": "2026-12-01",
  "locale": "en",
  "cursor": null,
  "limit": 10
}
{
  "items": [
    {
      "schema_version": "1.0",
      "ref": "off_diu_pdds",
      "external_ref": "PDDS",
      "connector": "commerce_api",
      "kind": "course_intake",
      "title": "Professional Diploma in Data Science",
      "subtitle": "January 2027 intake",
      "description": "A one-year evening programme covering Python, statistics, machine learning and a capstone project.",
      "category": "Professional diplomas",
      "tags": [
        "data science",
        "python",
        "machine learning"
      ],
      "availability_mode": "seats",
      "availability": {
        "status": "seats_available",
        "seats_available": 12,
        "seats_total": 40,
        "checked_at": "2026-09-15T11:00:05Z",
        "display": "12 of 40 seats left"
      },
      "selection_schema": {
        "cohorts": [
          {
            "ref": "coh_diu_pdds_2027_01",
            "external_ref": "PDDS-2027-JAN",
            "label": "January 2027 intake",
            "starts_on": "2027-01-10",
            "ends_on": "2027-12-16",
            "application_deadline": "2026-12-15",
            "seats_total": 40,
            "seats_available": 12
          }
        ],
        "quantity": {
          "min": 1,
          "max": 1
        }
      },
      "required_fields": [
        {
          "key": "name",
          "label": "Full name as on your certificate",
          "group": "learner",
          "input": "text",
          "required": true
        },
        {
          "key": "phone",
          "label": "Phone number",
          "group": "contact",
          "input": "phone",
          "required": true
        },
        {
          "key": "email",
          "label": "Email",
          "group": "contact",
          "input": "email",
          "required": true
        },
        {
          "key": "highest_degree",
          "label": "Highest degree",
          "group": "learner",
          "input": "text",
          "required": true
        },
        {
          "key": "graduation_year",
          "label": "Graduation year",
          "group": "learner",
          "input": "number",
          "required": true
        }
      ],
      "quote_required": false,
      "price": {
        "amount": "45000.00",
        "currency": "BDT"
      },
      "price_label": "tuition, full programme",
      "fulfilment_kind": "attendance",
      "images": [
        "https://admissions.diu.example/media/pdds-cover.jpg"
      ],
      "page_url": "https://admissions.diu.example/programmes/pdds",
      "disclosures": [
        "Tuition of ৳45,000.00 is payable by bank deposit within 14 days of acceptance.",
        "Seat numbers are a snapshot; DIU confirms the seat when it accepts the application."
      ],
      "freshness_at": "2026-09-15T11:00:05Z"
    }
  ],
  "next_cursor": null,
  "total_estimate": 1,
  "freshness_at": "2026-09-15T11:00:05Z"
}
{
  "lines": [
    {
      "line_ref": "line_1",
      "offering_ref": "off_diu_pdds",
      "external_ref": "PDDS",
      "title": "Professional Diploma in Data Science",
      "quantity": 1,
      "cohort_ref": "coh_diu_pdds_2027_01"
    }
  ]
}
{
  "results": [
    {
      "line_ref": "line_1",
      "available": true,
      "availability_mode": "seats",
      "seats_available": 12,
      "current_price": {
        "amount": "45000.00",
        "currency": "BDT"
      }
    }
  ],
  "checked_at": "2026-09-15T11:03:20Z"
}

Status events

When an order changes (accepted, paid, fulfilled, cancelled), your system posts a signed event to Central AI. Central AI’s endpoints for your system:

MethodPathWhat
GET/.well-known/ucpCentral AI's UCP platform profile. keys[] holds the public key our requests are signed with.
POST/webhooks/commerce/{connection_ref}/Where your system posts signed status events. The URL is shown on your connection.
POST/webhooks/commerce/The webhook_url in our platform profile. Routed by the event's idempotency_key.

Event types: commitment.accepted, commitment.canceled, commitment.created, commitment.rejected, commitment.updated, fulfilment.updated, payment.updated. Events carry Webhook-Id (the event id, stable across retries) and Webhook-Timestamp, both signed. A redelivered event is recognised and not applied twice, and an event older than the last one applied changes nothing.

  • An event moves the three statuses (acceptance, payment, fulfilment) and the amount paid. It never changes the price, the items, the customer or the payment page.
  • The customer is told in the chat when an order is accepted, rejected or cancelled, paid, refunded or failed, and when it is fulfilled.
Example event: an application accepted
{
  "event_id": "evt_diu_5501",
  "delivery_id": "dlv_diu_5501_1",
  "type": "commitment.accepted",
  "occurred_at": "2026-09-15T13:40:00Z",
  "reference": "AD-311",
  "idempotency_key": "ca-op-01J8ZR7D5W3N8P1K6T2Y4M9Q0A",
  "attribution": {
    "operation_ref": "op_01J8ZR7D5W3N8P1K6T2Y4M9Q0A"
  },
  "statuses": {
    "acceptance": "accepted",
    "payment": "unpaid",
    "fulfilment": "not_started"
  },
  "previous_statuses": {
    "acceptance": "pending",
    "payment": "unpaid",
    "fulfilment": "not_started"
  },
  "commitment": {
    "reference": "AD-311",
    "kind": "enrollment",
    "statuses": {
      "acceptance": "accepted",
      "payment": "unpaid",
      "fulfilment": "not_started"
    },
    "terms_version": 1,
    "amounts": {
      "total": {
        "amount": "45000.00",
        "currency": "BDT"
      },
      "paid": {
        "amount": "0.00",
        "currency": "BDT"
      },
      "due": {
        "amount": "45000.00",
        "currency": "BDT"
      }
    },
    "payment": {
      "code": "bank_deposit",
      "label": "Bank deposit to the DIU account",
      "collected_offline": true
    },
    "provider_status": {
      "application_status": "accepted",
      "fee_status": "unpaid"
    },
    "created_at": "2026-09-15T11:07:31Z",
    "updated_at": "2026-09-15T13:40:00Z",
    "note": "Upload your graduation certificate on the admissions portal before 15 December 2026."
  },
  "contract_version": "1.0"
}

Taking payment with your own gateway

If your system already takes payment online (SSLCommerz, Stripe, bKash or Nagad checkout, or any other gateway), the customer pays there. Central AI never sees card details and never holds money.

  1. In your quote.calculate answer, list the payment methods your system takes, with "collected_offline": false for an online gateway (for example {"code": "sslcommerz", "label": "Card, bKash or Nagad (SSLCommerz)", "collected_offline": false}). The agent offers exactly these.
  2. When commit.create creates the order, start the payment in your gateway as you do on your own checkout (an SSLCommerz session, a Stripe Checkout Session) and return its page in the commitment as continue_url (https), in that same answer. The customer’s order card shows Pay now, opening that page, and says Waiting for your payment.
  3. When your gateway confirms the payment (its IPN or webhook to your system), post a status event with statuses.payment set to paid (and commitment.amounts.paid). The customer is told in the chat, and the order shows Paid. A failed or cancelled payment is failed, and the order card says so.

The payment page is taken only from your signed commit.create answer. A status event can never set or change it, so a forged or replayed event cannot send a customer to another page.

Example: a quote offering SSLCommerz and Stripe
{
  "quote": {
    "schema_version": "1.0",
    "quote_ref": "quote_goedu_7710",
    "version": 1,
    "selection_ref": "sel_goedu_2Kp4",
    "selection_revision": 1,
    "selection_fingerprint": "9a1f6c2d4b8e0f3a5c7d9e1b3f5a7c9e1d3b5f7a9c1e3d5b7f9a1c3e5d7b9f1a",
    "currency": "BDT",
    "mode": "binding",
    "items": [
      {
        "line_ref": "line_1",
        "offering_ref": "off_goedu_webdev",
        "external_ref": "WEB-101",
        "title": "Full-Stack Web Development",
        "variant_label": "November 2026 batch",
        "quantity": 1,
        "unit_price": {
          "amount": "12500.00",
          "currency": "BDT"
        },
        "amount": {
          "amount": "12500.00",
          "currency": "BDT"
        }
      }
    ],
    "fees": [],
    "discounts": [],
    "subtotal": {
      "amount": "12500.00",
      "currency": "BDT"
    },
    "total": {
      "amount": "12500.00",
      "currency": "BDT"
    },
    "payment_methods": [
      {
        "code": "sslcommerz",
        "label": "Card, bKash or Nagad (SSLCommerz)",
        "collected_offline": false
      },
      {
        "code": "stripe",
        "label": "International card (Stripe)",
        "collected_offline": false
      }
    ],
    "expires_at": "2026-10-01T10:30:00Z",
    "disclosures": [
      "The seat is held for you for 30 minutes after you confirm, while you pay."
    ],
    "calculated_at": "2026-10-01T10:00:00Z"
  }
}
Example: the order created, with its payment page (continue_url)
{
  "idempotency_key": "ca-op-01K2GEDU7M3N8P1K6T2Y4M9Q0B",
  "replayed": false,
  "commitment": {
    "reference": "ENR-2041",
    "kind": "enrollment",
    "statuses": {
      "acceptance": "accepted",
      "payment": "unpaid",
      "fulfilment": "not_started"
    },
    "terms_version": 1,
    "amounts": {
      "total": {
        "amount": "12500.00",
        "currency": "BDT"
      },
      "paid": {
        "amount": "0.00",
        "currency": "BDT"
      },
      "due": {
        "amount": "12500.00",
        "currency": "BDT"
      }
    },
    "payment": {
      "code": "sslcommerz",
      "label": "Card, bKash or Nagad (SSLCommerz)",
      "collected_offline": false
    },
    "continue_url": "https://pay.goedu.example/checkout/ENR-2041",
    "continue_url_expires_at": "2026-10-01T10:37:31Z",
    "created_at": "2026-10-01T10:07:31Z",
    "updated_at": "2026-10-01T10:07:31Z",
    "note": "Your seat is held for 30 minutes while you pay."
  },
  "attribution": {
    "operation_ref": "op_01K2GEDU7M3N8P1K6T2Y4M9Q0B"
  }
}
Example: the event that says it is paid
{
  "event_id": "evt_goedu_9120",
  "delivery_id": "dlv_goedu_9120_1",
  "type": "payment.updated",
  "occurred_at": "2026-10-01T10:12:04Z",
  "reference": "ENR-2041",
  "idempotency_key": "ca-op-01K2GEDU7M3N8P1K6T2Y4M9Q0B",
  "attribution": {
    "operation_ref": "op_01K2GEDU7M3N8P1K6T2Y4M9Q0B"
  },
  "statuses": {
    "acceptance": "accepted",
    "payment": "paid",
    "fulfilment": "not_started"
  },
  "previous_statuses": {
    "acceptance": "accepted",
    "payment": "unpaid",
    "fulfilment": "not_started"
  },
  "commitment": {
    "reference": "ENR-2041",
    "kind": "enrollment",
    "statuses": {
      "acceptance": "accepted",
      "payment": "paid",
      "fulfilment": "not_started"
    },
    "terms_version": 1,
    "amounts": {
      "total": {
        "amount": "12500.00",
        "currency": "BDT"
      },
      "paid": {
        "amount": "12500.00",
        "currency": "BDT"
      },
      "due": {
        "amount": "0.00",
        "currency": "BDT"
      }
    },
    "payment": {
      "code": "sslcommerz",
      "label": "Card, bKash or Nagad (SSLCommerz)",
      "collected_offline": false,
      "reference": "SSLCZ-8F3K2Q",
      "paid_at": "2026-10-01T10:12:00Z"
    },
    "created_at": "2026-10-01T10:07:31Z",
    "updated_at": "2026-10-01T10:12:04Z",
    "note": "Payment received. Class links arrive by email before the batch starts."
  },
  "contract_version": "1.3"
}

Without a Commerce API connection you have three simpler options: Live checkout on your site, where the chat fills your cart, your checkout takes the payment and one signed webhook tells the chat; On our page, so the agent helps the customer choose and the card’s button opens your own checkout (where your gateway already is); or use Handled in Central AI with the Online payment method and your payment page link, and record the payment when it arrives. See How customers pay.

Errors

Refuse with the error.v1 envelope and the right category: it decides what Central AI does next, and what the customer is told.

CategoryWhat Central AI does
validationThe request was malformed or failed your checks. Nothing was created.
authenticationThe signature or the key did not verify. Nothing was created.
authorizationThe customer must finish directly with you (send a continue_url).
unsupported_capabilityYou do not offer this operation.
version_conflictYou do not speak the contract version Central AI sent.
price_changedThe price moved: Central AI quotes again and asks the customer.
out_of_stockSold out, full or taken: the customer is told and offered something else.
hold_expiredA held seat or slot lapsed: Central AI quotes again.
rate_limitedToo many calls (HTTP 429). On an order Central AI runs commit.lookup before anything else, so the order is never sent twice.
unavailable_readYou could not answer a read just now: the agent says it cannot confirm it yet.
definite_write_rejectionYou refused the order and created nothing. A corrected order may follow under a new key.
uncertain_writeYou cannot say whether it was created: Central AI runs commit.lookup before anything else.

Business refusal codes the agent explains to the customer: hold_expired, insufficient_quantity, out_of_stock, price_changed, quote_mismatch, seats_full, slot_taken, sold_out, unavailable.

Protocol error codes (HTTP status, category, retryable)
CodeHTTPCategoryRetry
idempotency_conflict409definite_write_rejectionno
idempotency_in_flight409uncertain_writeyes
storage_unavailable503uncertain_writeyes
complete_in_progress409uncertain_writeyes
requires_escalation409authorizationno
version_unsupported400version_conflictno
key_rotated401authenticationyes
unknown_kid401authenticationyes
idempotency_key_expired409definite_write_rejectionno
signature_missing401authenticationno
signature_invalid401authenticationno
key_not_found401authenticationyes
digest_mismatch400validationno
algorithm_unsupported400validationno
invalid_profile_url400validationno
profile_unreachable424unavailable_readyes
profile_not_trusted403authorizationno

Connect it

  1. On AI Sales Agent, turn on Advanced in section 3; in Connection to your system (company admins), choose My own website, app or booking system (Commerce API).
  2. Enter the profile address (https) and the key your system issued for Central AI. The key is stored encrypted and never shown again. Press Save connection.
  3. Press Publish connection revision. Central AI reads and freezes your profile and pins its keys.
  4. Press Test connection. It re-reads the profile, sends one test order and looks it up, then shows a report. Orders are sent only while the last test passed.
  5. In section 3, set the kinds your system handles to Created in our system, and publish the settings.

Go-live checklist

  • Your offerings are in Central AI with the id they have in your system.
  • quote.calculate answers with your real prices, delivery and the payment methods you accept.
  • The same Idempotency-Key and body replays your first answer; your keys are kept at least 7 days.
  • Every response and event is signed with the key in your discovery document; the signature test vectors pass.
  • An online payment returns continue_url, and your gateway’s confirmation posts a paid event.
  • Test connection passed, the kinds are set to Created in our system, and one real order from the Playground reached your system and came back as events.

Conventions

  • Money is {"amount": "1250.00", "currency": "BDT"}: an exact decimal string and a currency code, never a JSON number.
  • Times are UTC ISO 8601 ending in Z; dates are YYYY-MM-DD.
  • Links are absolute https:// addresses only.
  • Errors use the error.v1 envelope: code, category, safe_message, retryable, retry_after_seconds, operation_ref, correlation_id.

Signed-in administrators see every header, status and error code on the API reference.

Signed in? Your own keys, endpoint addresses and the full error tables are on the API reference inside your dashboard. These public pages never show anyone’s key: examples use placeholders such as YOUR_EXTERNAL_API_KEY.