Commerce reference
The Commerce API is workspace-scoped and lives at https://api.commerce.destesi.io.
Authentication
Section titled “Authentication”Two ways in, checked in this order:
# 1. Personal access token — requires the workspace headercurl https://api.commerce.destesi.io/v1/products \ -H "Authorization: Bearer idn_pat_xxxxxxxx" \ -H "X-Destesi-Workspace: your-workspace-slug"# 2. Session cookie — what the web app usescurl https://api.commerce.destesi.io/v1/products \ -H "Cookie: commerce_session=..."Mint a token from your account. Notes that bite:
- The workspace header is required with a token. Without
X-Destesi-Workspace, the request is rejected — a token by itself doesn’t say which workspace you mean. - A malformed bearer is rejected outright.
Authorization: Bearer something-elsereturns401 unknown_bearer_scheme; it does not fall back to the cookie. - The session cookie is host-only on the API subdomain and lasts 30 days.
Errors are always JSON: {"error":"<code>"}, sometimes with a detail field.
Endpoint groups
Section titled “Endpoint groups”| Group | Representative routes |
|---|---|
| Catalog | GET /v1/products, `GET |
| Orders | `GET |
| Logistics | PUT /v1/orders/{id}/shipping-address, `GET |
| Campaigns | /v1/campaigns, /v1/campaigns/{id}/ads, /publish, /boost, /insights, /v1/strategies, POST /v1/content-sets (a launch: one draft piece per placement, each with its own ref code), GET /v1/content-sets/{id}, POST /v1/content-sets/{id}/captions (the whole launch’s copy, written in one pass from its brief), POST /v1/campaigns/{id}/mark and /unmark (your own record that you published a bio-link or WhatsApp-Status placement by hand — kept apart from a platform-confirmed publication), GET /v1/content-sets/{id}/results?since=&until= (what the launch produced per placement inside an explicit window; the unattributed row sits outside the totals and every metric carries a measurement state, so what cannot be measured reads as unavailable rather than as zero) |
| Audiences | `GET |
| Images | POST /v1/image-jobs (queue a render — it takes ~2 minutes, so it is a job you poll, and it never overwrites the current picture), GET /v1/image-jobs, GET /v1/image-jobs/{id}, POST /v1/image-jobs/{id}/cancel (only while still queued) |
| Operator chat | POST /v1/operator/chat, POST /v1/operator/chat/resume, /v1/operator/conversations, POST /v1/operator/uploads |
| Catalog source | `GET |
| Settings | /v1/settings/profile, /v1/settings/payments, /v1/settings/shipping, /v1/payments/status, /v1/setup/readiness |
| Notifications | GET /v1/notifications/deliveries (the per-destination delivery ledger). The bell itself is the suite’s, on your account |
Everything above requires authentication and is scoped to your workspace. An id belonging to another workspace returns 404 — never a 403, which would confirm the id exists.
Paid audiences
Section titled “Paid audiences”Saved audiences belong to exactly one provider. The provider cannot change after creation, and applying an audience copies its targeting into that campaign destination; later edits do not rewrite existing campaigns.
| Provider | Targeting shape |
|---|---|
| Meta Ads | Root fields: countries, regions, cities, minimum/maximum age, gender, and resolved interest ids. Empty targeting defaults to Colombia, 18+. |
| TikTok Ads | tiktok_ads.locations, official age groups (AGE_18_24 through AGE_55_100), gender, and resolved interest keyword ids. Custom targeting requires a location; {} is a broad audience. |
Search results are provider-native. Always pass the same provider when searching and creating the audience; an id returned by Meta is not valid for TikTok and vice versa.
Public endpoints
Section titled “Public endpoints”These are deliberately outside the session/token check, because the caller is a payment provider, a carrier, or a buyer holding a link.
| Route | Who calls it |
|---|---|
POST /v1/webhooks/wompi |
Wompi, on a payment event. Signature-verified and idempotent. |
POST /v1/webhooks/skydropx |
The carrier, on a tracking event. |
GET /v1/storefront/products/{id} |
A buyer opening a product link. |
POST /v1/storefront/checkout/{order,shipping,pay} |
A buyer completing a checkout link, authorized by the opaque code in the link. |
| `GET | POST /v1/storefront/bodega/{token}` |
Order states
Section titled “Order states”| State | Terminal | Can move to |
|---|---|---|
draft |
no | pending_payment, cancelled |
pending_payment |
no | paid, payment_failed, cod_pending, expired, cancelled |
cod_pending |
no | paid, cancelled |
payment_failed |
no | pending_payment, cancelled |
paid |
no | fulfilled |
fulfilled |
yes | — |
cancelled |
yes | — |
expired |
yes | — |
Any other move returns 409 illegal_transition. Orders in draft, pending_payment, and payment_failed still hold their reserved stock.
Plan limits
Section titled “Plan limits”| Free | Pro | Team | |
|---|---|---|---|
| Sales channels | 1 | 3 | All |
| Products (SKUs) | 100 | 2,500 | 10,000 |
| Sales-agent turns per month | 10 | 100 | 300 |
| Generated images per month | 5 | 100 | 500 |
| Campaign publishes per month | 5 | 100 | 500 |
Orders and sales volume are measured but are neither capped nor charged for. When the agent-turn allowance runs out, buyers get one message telling them a person will follow up and the conversation is routed to you — it does not go silent. See Plans and billing.
Settings that change behaviour
Section titled “Settings that change behaviour”Every setting below is read by any member and changed only by an owner or admin
(admin_required otherwise), as is linking an ad account or a Telegram chat. Which WhatsApp
number answers buyers is chosen in Inbox. See Roles and permissions.
| Setting | Effect |
|---|---|
| Payment mode | Resolved automatically in precedence order: your own Wompi account → Destesi’s account (only if granted) → bank transfer → cash on delivery → disabled. GET /v1/payments/status reports the active one. |
| Warehouse origin | Required before any shipping quote. Missing → quotes are refused. |
| Product weight | Required per product for a quote. Missing → 422 weight_incomplete. |
| Reservation window | 30 minutes by default; can be set per order at creation. |
| Catalog source | local (you build the catalog here) or shopify (a linked store owns name, description, price, stock, status, photos and variants for the products it brought). Editing one of those returns 422 field_managed_by_source; everything else on the product — section, audience, spec sheet, parcel, video — stays yours. |
| Bre-B llave | Adds a scannable QR to the bank-transfer instructions. |
Connected services
Section titled “Connected services”Commerce reads every third-party credential from Connect — it stores none of its own. Reference-photo image rendering uses local Codex when the operator enables it. Otherwise it is unavailable until an image editor is configured. An operator may explicitly select the legacy OpenAI editor; that mode is billed outside AWS.
| Provider | Enables | Without it |
|---|---|---|
| The buyer-facing sales agent, inbound and outbound | Replies are composed and stored but never delivered | |
| Meta Ads | Publishing and measuring campaigns | 422 meta_settings_not_set |
| TikTok Ads | Publishing video campaigns with provider-native audiences | settings_not_set, channel_unavailable, or publish_failed |
| Skydropx | Quotes, labels, tracking, cash on delivery | 503 carrier_disabled |
| Wompi | Card and PSE payment links | Falls through to bank transfer or cash on delivery |
| Gmail | Automatic bank-transfer confirmation | Falls back to asking you on WhatsApp |
| OpenAI (legacy opt-in) | Editing product imagery from a reference photo when the operator explicitly selects OpenAI | openai_not_connected |
| Shopify | Importing an existing store’s catalog, reading a product live, reading what the store sold | 409 catalog_source_not_shopify — connect the store in Connect, then link it |
Related
Section titled “Related”- Quickstart · Concepts · Troubleshooting
- Connect for the credentials above · CLI & API auth for tokens