Authentication
Send your API key with every call. The key decides which store you read and which endpoints you may call.
Send the key
Authorization: Bearer <your key>
Send it on every request, over HTTPS. Only the status calls and the OpenAPI document need no key.
- No key, or a key that is not a key:
401 unauthorized. - A revoked key:
401 key_revoked. - A key that may not call this endpoint:
403 capability_not_enabled.
Keys
You create and revoke keys at account.compudime.com. A key belongs to one store.
| Key starts with | What it is |
|---|---|
cc_test_ or cd_test_ | A test key. Its calls use the paying organization's monthly sandbox allowance, never overage, and work before the store has a card on file. The billing block says "mode": "test". Its orders are simulated and never reach the store (see Test orders). |
cc_live_ or cd_live_ | A live key. Its calls are billed to the paying organization's plan. |
Both read the same store data. Only orders differ: a test key's orders never reach the store. Build with a test key, and switch to a live key when you go live. cd_ keys are older keys; they keep working.
Working across stores
A developer who builds for many stores gets one key per store, once each store's owner approves. There is no key for several stores: for ten stores you hold ten keys, and GET /v1/me names the store a key reads.
- Approval. CompuDime opens your developer organization with the stores you may reach and the capabilities you may use there: write to support@compudime.com to be opened, or to reach more stores. Then you ask each store yourself, on the Stores page at account.compudime.com: pick the store, the capabilities you need and why (the store's owner reads your reason), and agree to the developer terms. A store can be asked only once its owner has opened it to third-party developers; when the store is closed to developers, outside the stores you may reach, or a capability is not on for it, the page says which. The store's owners and billing admins get your request by email and in their CompuConnect account, and there they approve it as asked, approve less, or decline it. CompuDime can also record an owner's answer on the owner's behalf. The approval names the capabilities you may use on that store. Then you create the store's key at account.compudime.com, and it carries those capabilities and no more.
- Who pays. Your organization pays for the calls, unless the store owner chose to pay when approving. Either way your key never sees the store's plan, card or credits:
GET /v1/meleavescreditsout and links your organization's usage inusageUrl, andGET /v1/usage/currentanswers403 usage_not_available_for_key. Your organization's usage is at account.compudime.com/usage. When the store pays, each call still says what it cost, but not the store's balance: there is noX-Credits-Remaining, and when the store cannot be charged you get402 payer_billing_blocked, whatever the reason. - The owner's settings stay with the approval, not the key. Your access level, whether you get customer account figures, and your field policy are the store owner's choice. Replace a key and the new key reads exactly as the old one did, with the same
owncustomers and the same webhook receivers. A new approval starts again atown, without account figures. - One budget. The keys of one approval share one rate limit for each capability: one for its live keys, another for its test keys.
- Test keys read apart. An approval's test keys read through their own integration for each capability, never the live keys'. They start at
own, without account figures, withowncustomers and webhook receivers of their own; a replacement test key keeps them. - Withdrawn. When the owner withdraws the approval, its keys stop for good at once, on their very next call:
401 key_revoked. Its webhook receivers, live and test, are removed at the same moment: no order event is sent to them afterwards, not even a retry that was waiting.
Test orders
An order you send with a test key (POST /v1/orders) is checked exactly like a live key's order: the body, the lines, the Idempotency-Key and what your key may do. You get the same 202 and the same body. But the order is simulated. It never reaches the store's POS.
orderIdstarts withtest_. A live order's id never does.dispatchStateissimulated. It never changes.- The
billingblock says"mode": "test". The order uses the sandbox allowance like any other test call. - No webhook is sent for it.
- Send the same
Idempotency-Keyagain and you get the same simulated order back. - Read it with
GET /v1/orders/{orderId}orGET /v1/orders/external/{externalOrderId}and a test key. A live key never sees a test order, and a test key never sees a live order: you get404 order_not_found.
What a test order does not check: the store looks up each productCode and sets the prices only when a live order reaches it. A simulated order proves your request is right, not that the store carries the products.
Request ids
Send X-Request-Id with an id of your own: 1 to 128 letters, digits, dots, underscores or dashes, starting with a letter or digit. It comes back as requestId in the body and in the X-Request-Id header. Without one, the API makes one up. Log it, and quote it when you write to support.
What a key may do
A key carries one or more capabilities. Each endpoint needs one. GET /v1/capabilities is free and says what your key carries. The store owner decides which capabilities a store turns on.
GET /v1/me, also free, describes the key itself: its integration and access level, its store, the fields it may ask for on each endpoint it can call, its rate limit and, for the store's own keys, the store's credits. It never shows the key.
accounting-data
- GET
/v1/accounting/journal-entriesList mapped journal entries by store-day live - GET
/v1/accounting/mappingGet the store's account mapping keys live - GET
/v1/accounting/sales-summariesList register-log sales summaries by store-day live
catalog-feed
- GET
/v1/brandsList the store's brands live - GET
/v1/catalog/snapshots/currentGet current catalog snapshot - GET
/v1/categoriesList the store's departments, categories and sub-categories live - GET
/v1/productsList catalog products - GET
/v1/products/lookup/{code}Look up a product live by any code - GET
/v1/products/{productCode}Get catalog product - GET
/v1/promotionsList the store's promotions live - GET
/v1/promotions/mix-match/{groupId}Get a mix and match group live - GET
/v1/reasonsList the register's discount and return reasons live - GET
/v1/storeGet the store's business profile live - GET
/v1/tagsList the store's product tags live
customer-access
- GET
/v1/customersList the store's customers live - GET
/v1/customers/lookupLook up POS customers by phone - GET
/v1/customers/{posCustomerId}Get a customer live by POS customer id - GET
/v1/sales/dailyList daily register totals - GET
/v1/tendersList the register's tender types
sales-data
- GET
/v1/delivery/routesList the store's delivery routes live - GET
/v1/inventory/onhandList on-hand quantities live - GET
/v1/invoicesList the store's POS invoices live - GET
/v1/invoices/external/{externalId}Get an invoice live by the placing system's order id - GET
/v1/invoices/{posInvoiceId}Get an invoice live by POS invoice id - GET
/v1/sales/movementList sales movement - GET
/v1/sales/zreportGet the register Z reports of a business day live - GET
/v1/ticketsList the store's register tickets live - GET
/v1/tickets/{ticketId}Get a register ticket live with its lines and tenders
order-intake
- POST
/v1/ordersCreate order intake request - GET
/v1/orders/external/{externalOrderId}Get order intake status by external order id - GET
/v1/orders/{orderId}Get order intake status - POST
/v1/webhooksRegister a webhook receiver - POST
/v1/webhooks/{webhookId}/rotate-secretRotate a receiver's signing secret - POST
/v1/webhooks/{webhookId}/testSend a test event to a receiver
purchasing-data
- GET
/v1/purchase-ordersList the store's vendor purchase orders live - GET
/v1/purchase-orders/{posPurchaseOrderId}Get a purchase order live by POS purchase order id - GET
/v1/receivingList the store's receiving documents live - GET
/v1/receiving/{posReceivingId}Get a receiving document live by POS receiving id
Each endpoint also names a scope (x-compudime-scope in the reference). Scopes come with the capability; you do not ask for them.
Access levels: own or all customers
Every integration has an access level, own or all. The store owner sets it. GET /v1/capabilities returns it as accessLevel. It changes the customer reads and nothing else.
- all reads the store's whole customer file. It is the sensitive level, and the owner grants it on purpose.
- own reads only the customers linked to your integration: the customers your own orders landed on in the store's POS, matched by the customer's main phone. With a
cc_key, your own orders are the orders sent with that key, or with any key of the same store approval (see Working across stores). A new integration isownunless the owner makes itall.
A customer outside your level is answered like a customer the store does not have. You never get a 403 for it, so an answer never tells you a customer exists. A cursor belongs to the level it was made at: if the owner changes your level, start the listing again.
| Endpoint | At own | At all |
|---|---|---|
GET /v1/accounting/journal-entries | The level does not change this operation: own and all return the same data. Scope accounting.read. | The level does not change this operation: own and all return the same data. Scope accounting.read. |
GET /v1/accounting/mapping | The level does not change this operation: own and all return the same data. Scope accounting.read. | The level does not change this operation: own and all return the same data. Scope accounting.read. |
GET /v1/accounting/sales-summaries | The level does not change this operation: own and all return the same data. Scope accounting.read. | The level does not change this operation: own and all return the same data. Scope accounting.read. |
GET /v1/brands | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. |
GET /v1/capabilities | The level does not change this operation: own and all return the same data. Scope platform.capabilities.read. | The level does not change this operation: own and all return the same data. Scope platform.capabilities.read. |
GET /v1/catalog/snapshots/current | The level does not change this operation: own and all return the same data. Scope catalog.snapshots.read. | The level does not change this operation: own and all return the same data. Scope catalog.snapshots.read. |
GET /v1/categories | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. |
GET /v1/customers | Pages only the customers linked to this integration: the customers its own orders (POST /v1/orders) landed on in the store's POS, matched by the customer's main phone. They come in phone order, at most 25 a page whatever limit asks, and a page can hold fewer (even none) while nextCursor is set, when a linked customer is no longer on file. The cursor is this level's own; a cursor issued at the other level is a 400. modifiedSince keeps the linked customers modified at or after it, and every linked customer without a modifiedAt. An integration with no linked customer gets an empty page. Scope customers.read.self. | Pages the store's whole customer file. Scope customers.read.all. |
GET /v1/customers/lookup | Returns only the customers linked to this integration among the phone's matches. When none is linked the answer is found:false with no customers, exactly like a phone the store does not know, so it never confirms that a customer exists. Scope customers.read.self. | Returns every customer on file for the phone. Scope customers.read.all. |
GET /v1/customers/{posCustomerId} | Returns the customer only when it is linked to this integration (its main phone is the phone of one of this integration's orders that landed in the POS). Any other customer is 404 customer_not_found, exactly like a customer the store does not have, so the answer never confirms that a customer exists. Scope customers.read.self. | Returns any customer of the store. Scope customers.read.all. |
GET /v1/delivery/routes | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/inventory/onhand | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/invoices | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/invoices/external/{externalId} | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/invoices/{posInvoiceId} | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/me | The level does not change this operation: own and all return the same data. Scope platform.me.read. | The level does not change this operation: own and all return the same data. Scope platform.me.read. |
POST /v1/orders | The order belongs to this integration at either level, and only this integration reads it back. Scope orders.write. | The order belongs to this integration at either level, and only this integration reads it back. Scope orders.write. |
GET /v1/orders/external/{externalOrderId} | Only the orders this integration created, at either level: all does not widen the order reads. Scope orders.read. | Only the orders this integration created, at either level: all does not widen the order reads. Scope orders.read. |
GET /v1/orders/{orderId} | Only the orders this integration created, at either level: all does not widen the order reads. Scope orders.read. | Only the orders this integration created, at either level: all does not widen the order reads. Scope orders.read. |
GET /v1/products | The level does not change this operation: own and all return the same data. Scope catalog.products.read. | The level does not change this operation: own and all return the same data. Scope catalog.products.read. |
GET /v1/products/lookup/{code} | The level does not change this operation: own and all return the same data. Scope catalog.products.read. | The level does not change this operation: own and all return the same data. Scope catalog.products.read. |
GET /v1/products/{productCode} | The level does not change this operation: own and all return the same data. Scope catalog.products.read. | The level does not change this operation: own and all return the same data. Scope catalog.products.read. |
GET /v1/promotions | The level does not change this operation: own and all return the same data. Scope catalog.products.read. | The level does not change this operation: own and all return the same data. Scope catalog.products.read. |
GET /v1/promotions/mix-match/{groupId} | The level does not change this operation: own and all return the same data. Scope catalog.products.read. | The level does not change this operation: own and all return the same data. Scope catalog.products.read. |
GET /v1/purchase-orders | The level does not change this operation: own and all return the same data. Scope purchasing.read. | The level does not change this operation: own and all return the same data. Scope purchasing.read. |
GET /v1/purchase-orders/{posPurchaseOrderId} | The level does not change this operation: own and all return the same data. Scope purchasing.read. | The level does not change this operation: own and all return the same data. Scope purchasing.read. |
GET /v1/reasons | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. |
GET /v1/receiving | The level does not change this operation: own and all return the same data. Scope purchasing.read. | The level does not change this operation: own and all return the same data. Scope purchasing.read. |
GET /v1/receiving/{posReceivingId} | The level does not change this operation: own and all return the same data. Scope purchasing.read. | The level does not change this operation: own and all return the same data. Scope purchasing.read. |
GET /v1/sales/daily | The level does not change this operation: own and all return the same data. Scope customers.read.all. | The level does not change this operation: own and all return the same data. Scope customers.read.all. |
GET /v1/sales/movement | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/sales/zreport | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/store | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. |
GET /v1/tags | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. | The level does not change this operation: own and all return the same data. Scope catalog.reference.read. |
GET /v1/tenders | The level does not change this operation: own and all return the same data. Scope customers.read.all. | The level does not change this operation: own and all return the same data. Scope customers.read.all. |
GET /v1/tickets | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/tickets/{ticketId} | The level does not change this operation: own and all return the same data. Scope sales.read. | The level does not change this operation: own and all return the same data. Scope sales.read. |
GET /v1/usage/current | The level does not change this operation: own and all return the same data. Scope platform.usage.read. | The level does not change this operation: own and all return the same data. Scope platform.usage.read. |
GET /v1/webhooks | The level does not change this operation: own and all return the same data. Scope webhooks.read. | The level does not change this operation: own and all return the same data. Scope webhooks.read. |
POST /v1/webhooks | The level does not change this operation: own and all return the same data. Scope orders.webhooks. | The level does not change this operation: own and all return the same data. Scope orders.webhooks. |
DELETE /v1/webhooks/{webhookId} | The level does not change this operation: own and all return the same data. Scope webhooks.write. | The level does not change this operation: own and all return the same data. Scope webhooks.write. |
GET /v1/webhooks/{webhookId} | The level does not change this operation: own and all return the same data. Scope webhooks.read. | The level does not change this operation: own and all return the same data. Scope webhooks.read. |
GET /v1/webhooks/{webhookId}/deliveries | The level does not change this operation: own and all return the same data. Scope webhooks.read. | The level does not change this operation: own and all return the same data. Scope webhooks.read. |
POST /v1/webhooks/{webhookId}/rotate-secret | The level does not change this operation: own and all return the same data. Scope orders.webhooks. | The level does not change this operation: own and all return the same data. Scope orders.webhooks. |
POST /v1/webhooks/{webhookId}/test | The level does not change this operation: own and all return the same data. Scope orders.webhooks. | The level does not change this operation: own and all return the same data. Scope orders.webhooks. |
Scoped keys
A key can carry a fixed data scope, set by the store owner:
- a field policy: which product fields the key may see;
- row rules: which products the key may see, such as
brand eq X. The rules useeq,inandprefixonproductCode,upc,brand,department,categoryandactive.
A scoped key pages, looks up and counts only inside its slice. There is no parameter that widens it. Reads whose totals cover every product of the store (invoices, delivery routes, purchase orders, receiving, register tickets and the Z report) answer a scoped key with 422 scope_not_applicable, which is never charged.