Ecommerce API and event contract¶
This contract specializes the common HTTP rules in the platform specification. The implementation repository must publish machine-readable OpenAPI and JSON Schemas that do not weaken this page.
Pilot reference downloads:
- Ecommerce Reporting API - OpenAPI 3.1
odoo.readback.confirmed.v1- JSON Schemawarehouse.mart.refreshed.v1- JSON Schema- Deployment and validation guide
HTTP conventions¶
- Base path is
/api/v1. - OAuth/OIDC platform identity is required; Odoo and source credentials never reach the browser.
- Tenant resources include
/tenants/{tenant_id}and enforce membership, role, and assignment server-side. - Money is a decimal string plus ISO 4217 currency.
- Timestamps are UTC RFC 3339; source business date and IANA timezone are explicit.
- List endpoints use opaque cursor pagination with deterministic ordering.
- Mutable review resources expose
resource_versionand requireIf-Match. - Commands require
Idempotency-Key, reject unknown properties, persist before queue delivery, and return202 Accepted. - No payload may select an arbitrary source adapter, SQL relation, Odoo model, method, fields, domain, or context.
Resource catalog¶
Source and ingestion operations¶
GET /api/v1/operations/tenants/{tenant_id}/source-capabilities
POST /api/v1/operations/tenants/{tenant_id}/source-capability-scans
GET /api/v1/operations/source-capability-scans/{scan_id}
GET /api/v1/operations/tenants/{tenant_id}/ingestion-runs
POST /api/v1/operations/tenants/{tenant_id}/ingestion-runs
GET /api/v1/operations/ingestion-runs/{run_id}
POST /api/v1/operations/ingestion-runs/{run_id}/replay
Commerce resources¶
GET /api/v1/tenants/{tenant_id}/commerce/orders
GET /api/v1/tenants/{tenant_id}/commerce/orders/{order_id}
GET /api/v1/tenants/{tenant_id}/commerce/orders/{order_id}/events
GET /api/v1/tenants/{tenant_id}/commerce/payments
GET /api/v1/tenants/{tenant_id}/commerce/fulfillments
GET /api/v1/tenants/{tenant_id}/commerce/refunds
GET /api/v1/tenants/{tenant_id}/commerce/returns
GET /api/v1/tenants/{tenant_id}/commerce/disputes
Policy, mapping, and posting commands¶
GET /api/v1/tenants/{tenant_id}/mapping-sets
GET /api/v1/tenants/{tenant_id}/mapping-exceptions
POST /api/v1/tenants/{tenant_id}/commerce/orders/{order_id}/posting-intents
POST /api/v1/tenants/{tenant_id}/commerce/posting-intents/{intent_id}/approve
POST /api/v1/tenants/{tenant_id}/commerce/posting-intents/{intent_id}/execute
GET /api/v1/tenants/{tenant_id}/operations/{operation_id}
Settlement and reporting resources¶
GET /api/v1/tenants/{tenant_id}/settlements
GET /api/v1/tenants/{tenant_id}/settlements/{settlement_id}
POST /api/v1/tenants/{tenant_id}/payout-matches/{match_id}/approve
POST /api/v1/tenants/{tenant_id}/payout-matches/{match_id}/post
GET /api/v1/tenants/{tenant_id}/reports/sales
GET /api/v1/tenants/{tenant_id}/reports/refunds
GET /api/v1/tenants/{tenant_id}/reports/fees
GET /api/v1/tenants/{tenant_id}/reports/settlements
GET /api/v1/tenants/{tenant_id}/reports/posting-health
GET /api/v1/tenants/{tenant_id}/reports/inventory
GET /api/v1/tenants/{tenant_id}/reports/data-freshness
The domain contracts define the complete command preconditions and response evidence.
Command envelope¶
{
"contract_version": "1.0",
"resource_version": 7,
"reason": "Approved Shopify pilot order",
"requested_at": "2026-07-02T10:00:00Z"
}
The tenant comes from the authorized route binding, not a duplicate body property. The server adds operation_id, correlation_id, actor, request hash, policy/mapping versions, and trusted connection IDs.
Command response:
{
"contract_version": "1.0",
"operation_id": "0190f5d4-7d75-7d22-9c7d-45d20e89ce6b",
"status": "queued",
"correlation_id": "corr_01J1Z8K0Q6VTX6V6YQ0B42T9N5",
"links": {
"self": "/api/v1/tenants/018f.../operations/0190..."
}
}
Reusing an idempotency key with the same canonical request hash returns the existing operation. Reusing it with a different hash returns 409 IDEMPOTENCY_KEY_REUSED.
Event envelope¶
{
"event_id": "0190f5dc-2f6b-7dc4-a609-fb9bfab163dd",
"event_type": "commerce.order.canonicalized.v1",
"schema_version": "1.0.0",
"tenant_id": "018f...",
"aggregate_type": "commerce_order",
"aggregate_id": "0190...",
"occurred_at": "2026-07-02T09:58:00Z",
"observed_at": "2026-07-02T10:00:00Z",
"correlation_id": "corr_01J1Z8K0Q6VTX6V6YQ0B42T9N5",
"causation_event_id": null,
"producer": "canonicalizer",
"payload": {}
}
event_id is globally unique. Consumer deduplication uses (consumer, event_id) and occurs before side effects. Events may be delayed, duplicated, or delivered out of order. Consumers use aggregate version/evidence and never assume broker order is business order.
Event catalog¶
| Event type | Emitted when | Required consumers |
|---|---|---|
source.delivery.accepted.v1 |
Webhook/Airbyte record is durably accepted | Canonicalizer, ingestion metrics |
source.capability.changed.v1 |
Source capability status/evidence changes | Enablement, operations |
commerce.order.canonicalized.v1 |
Canonical order projection commits | Policy/mapping evaluator, marts |
commerce.payment.observed.v1 |
Payment transaction projection commits | Settlement allocator, marts |
commerce.fulfillment.observed.v1 |
Fulfillment projection commits | Posting policy, inventory, marts |
commerce.refund.observed.v1 |
Refund projection commits | Refund policy, settlement, marts |
commerce.return.observed.v1 |
Return projection commits | Inventory workflow, marts |
commerce.dispute.changed.v1 |
Dispute state/evidence changes | Accounting review, settlement |
mapping.set.approved.v1 |
Mapping set becomes immutable/active | Intent builder, drift monitor |
posting.intent.ready.v1 |
Intent passes policy/mapping checks | Approval/operation service |
integration.operation.changed.v1 |
Durable operation status changes | UI projection, verification, marts |
odoo.readback.confirmed.v1 |
Odoo result passes invariants | Canonical projection, settlement, marts |
settlement.balanced.v1 |
Settlement equation balances | Payout matcher, marts |
payout.reconciled.v1 |
Bank/Odoo clearing readback confirms | Close workflow, marts |
warehouse.mart.refreshed.v1 |
A mart partition passes controls and publishes | Reporting cache, freshness monitoring |
data.subject.restriction.changed.v1 |
Governance restriction changes | Export/mart redaction |
Domain events are facts, not commands. Consumers cannot infer authorization to mutate Odoo from event receipt.
Error contract¶
{
"error": {
"code": "MAPPING_REQUIRED",
"message": "A required approved mapping is missing.",
"correlation_id": "corr_01J1Z8K0Q6VTX6V6YQ0B42T9N5",
"retryable": false,
"details": {
"mapping_type": "tax",
"action": "review_mapping_exception"
}
}
}
Error messages expose safe business context only. They never include credentials, webhook URLs, raw payloads, SQL, Odoo tracebacks, or another tenant's identifiers.
| Status | Meaning |
|---|---|
400 |
Malformed JSON, unsupported property, invalid cursor |
401 |
Missing/invalid platform identity |
403 |
Identity lacks tenant/role/action permission |
404 |
Resource absent or concealed by isolation |
409 |
Idempotency mismatch, stale version, conflicting active operation |
422 |
Valid shape but failed business/accounting invariant |
429 |
Tenant/user/provider rate limit |
502 |
Definite upstream rejection before mutation |
503 |
Dependency unavailable before mutation |
202 |
Durable asynchronous command accepted |
Compatibility¶
- Additive optional response/event properties are backward compatible within a major version.
- New required command properties, changed amount/sign/identity semantics, removed fields, or changed state meanings require a new major contract.
- Producers publish the current and previous compatible event schema during rollout where consumers need migration time.
- Consumers reject unsupported major versions and quarantine invalid payloads.
- Schemas are registered by immutable
(event_type, schema_version, sha256)and validated in CI and at ingress.
Acceptance criteria¶
- OpenAPI and JSON Schemas validate all examples and reject unknown command properties.
- Tenant and role negative tests cover every resource and command.
- Idempotency replay, stale
If-Match, pagination stability, and version negotiation are tested. - Event producer/consumer tests cover duplicate, delay, out-of-order, invalid schema, and unsupported version.
- No HTTP or event payload permits arbitrary Odoo/source execution.