The Server API lets your own system send orders to Akeed as they are placed, instead of adding them by hand or importing a file. It is written for the developer who connects a custom website, an order system or a delivery back end.
Your server → POST /api/v1/orders → Akeed stores the order → WhatsApp confirmation → result in your dashboard
There is one endpoint, and it does one thing: it creates an order. From there the order follows the same confirmation flow as every other order in Akeed.
The API is available on standalone accounts. Shopify stores do not need it, because their orders arrive automatically. See Using Akeed Without Shopify.
An API key is a secret for your server only. Never put it in a web page, a mobile app or any code a customer can open. Anyone who has the key can send orders to your store.
Requests are sent over HTTPS with a JSON body in UTF-8.
A key belongs to one store. Every order sent with it goes to that store. There is no field for choosing another store or account, and a request that tries to send one is rejected.
This guide describes version 1 of the API, under /api/v1. A change that would break your integration will get a new version instead of changing this one.
Sign in to Akeed, open Settings → API keys and select Create key. Copy the key and save it on your server as AKEED_API_KEY. Save the endpoint address shown on the same screen, without the /api/v1/orders path, as AKEED_API_URL.
Open Confirmations in Akeed. The order is there, and the customer receives the WhatsApp confirmation message.
There is no test mode. An accepted cash-on-delivery order sends a real WhatsApp message and uses a credit. While you build, send orders with your own phone number.
externalOrderId is the order's identity. Use the id your own system already has for the order. Spaces, a leading # and the difference between capital and small letters are ignored, so #A-1001, a-1001 and A-1001 are the same order. Orders imported from a file use the same identity, so an order you import and later send through the API is still one order.
paymentMethod decides whether a message is sent. Akeed confirms cash-on-delivery orders only. Values such as cod, cash on delivery, cash_on_delivery, pay on delivery and الدفع عند الاستلام are read as cash on delivery. Any other value, such as Credit Card, is read as already paid.
Text is trimmed. Spaces at the start and end of a value are removed before it is checked and stored.
Unknown fields are rejected. A request with a field that is not in the table gets a 400, so a typing mistake in a field name cannot pass unnoticed.
orderNumber is what the customer reads. If your public order number differs from your internal id, send the internal id as externalOrderId and the public one as orderNumber.
A 202 response with "status": "accepted" means Akeed has stored the order safely. It does not mean the message was sent, delivered or answered. Those happen afterwards.
Field
Meaning
orderId
Akeed's id for the order. Keep it with your own order so support can find it.
status
Always accepted.
duplicate
false when this request created the order. true when Akeed already had it and nothing new was created.
verificationId
Akeed's id for the confirmation. It is present only once a confirmation exists, so it is usually missing from the first answer and never present for an order that is not cash on delivery.
What happens next depends on the order:
Cash on delivery: Akeed sends the WhatsApp confirmation, follows up if the customer does not reply, and marks the result. Your quiet hours and sending delay apply, as they do for every order.
Not cash on delivery: the order is stored and visible in your dashboard, but the customer is never messaged and no credit is used.
The result of every order is in your Akeed dashboard, in Confirmations: sent, delivered, read, confirmed, canceled, no reply or failed. See Order Confirmation for what each status means and what to do with it.
The API creates orders and does nothing else. You cannot change or cancel an order through it, you cannot ask it for an order's status, and Akeed does not call your server back when a customer replies. Use the dashboard to decide what to ship.
Networks fail, and jobs run twice. The API is built so that sending an order again is always safe: a customer never gets a second message, and you never pay twice.
The Idempotency-Key header names the order you are sending. Build it from your own order id, for example order- followed by the id, and use the same key every time you send that order, including every retry. Do not use a random value or the current time: a retry would then look like a new request.
Keys have a minimum length (see Limits), so use a longer prefix if your order ids are very short. A key belongs to your store, not to the API key that sent it, so it keeps working after you replace an API key.
Two requests carry the same order only when every field matches, including the optional ones. Sending city the second time when the first request had none is different order data. So is a different notes or orderNumber.
The order of the fields in the JSON does not matter, and neither do spaces around a value.
Write externalOrderId the same way every time. #10023 and 10023 are the same order, but when you send no orderNumber, the customer-facing number is taken from the id as you wrote it, so the two requests carry different order data.
For an order that came from a file import, compare with what the import stored. If the file had no order number for that row, Akeed gave the order a number that starts with IMP-, and a request for the same order matches only if it sends that value as orderNumber.
An order that is already in Akeed keeps its state when you send it again. If it belongs to an import that has not been started, it stays Awaiting start. If its import was stopped or expired before the order was sent, the customer is still not contacted. The answer is the usual duplicate: true in both cases, and no message is sent. The dashboard always shows the real state.
60 per minute for each store. All keys of a store share the limit, so a second key does not add requests.
Request size
32 KB for each request body.
Orders per request
One. There is no batch endpoint.
Idempotency-Key
8 to 128 characters: letters, numbers, dots, underscores, colons and hyphens.
A request over a limit is refused before Akeed looks at the order, so it never creates an order or uses a credit. These are the standard limits. If your store needs more, contact support before you go live.
Every example below is run by an automated test against a test instance of the API, so the statuses and bodies are exactly what you get. Values in angle brackets, such as <ORDER_ID>, are placeholders. $AKEED_API_URL and $AKEED_API_KEY are your endpoint address and your key; all order data is made up.
Your request timed out and you do not know whether Akeed received it. Send exactly the same request again, with the same Idempotency-Key. If the first one arrived, you get the same order back.
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "API_ORDER_IDEMPOTENCY_CONFLICT",
"message": "Idempotency-Key was already used with different order data.",
"correlationId": "<CORRELATION_ID>"
}
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "API_ORDER_EXTERNAL_ID_CONFLICT",
"message": "An order with this externalOrderId already exists with different order data.",
"correlationId": "<CORRELATION_ID>"
}
Order 10023 already exists. #10023 is the same order, but without an orderNumber the customer-facing number is taken from the id as written, so the data differs and the request is refused.
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "API_ORDER_EXTERNAL_ID_CONFLICT",
"message": "An order with this externalOrderId already exists with different order data.",
"correlationId": "<CORRELATION_ID>"
}
Sending the original number as orderNumber makes the data identical again:
A prepaid order is accepted and appears in the dashboard, but Akeed never messages the customer and no credit is used. The answer has no verificationId.
HTTP/1.1 413 Payload Too Large
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "API_PAYLOAD_TOO_LARGE",
"message": "The request body is too large.",
"correlationId": "<CORRELATION_ID>"
}
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
Retry-After: <SECONDS>
{
"code": "API_RATE_LIMITED",
"message": "Too many requests. Wait for the Retry-After period, then retry with the same Idempotency-Key.",
"correlationId": "<CORRELATION_ID>"
}
The store behind the key was deactivated or replaced.
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "API_SOURCE_UNAVAILABLE",
"message": "The store this API key belongs to cannot accept orders.",
"correlationId": "<CORRELATION_ID>"
}
The balance cannot cover one more message. Nothing is stored. Buy credits, then send the order again with the same key.
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
X-Correlation-Id: <CORRELATION_ID>
{
"code": "INSUFFICIENT_CREDITS",
"message": "Credit is not available for this action.",
"correlationId": "<CORRELATION_ID>"
}
fieldErrors appears only with API_VALIDATION_FAILED. correlationId is also sent in the X-Correlation-Id response header, on successful requests too.
Code
Status
What it means
What to do
API_VALIDATION_FAILED
400
A field, the Idempotency-Key header or the JSON itself is invalid. fieldErrors names each problem.
Fix the request. Sending it again unchanged fails the same way.
API_REQUEST_REJECTED
400
The request was refused for a reason that has no code of its own. The status can be any 4xx.
Check the method, path and headers against this guide.
API_KEY_INVALID
401
The key is missing, mistyped, revoked, sent in the URL, or not sent as Authorization: Bearer <key>.
Check the header. If the key was revoked or lost, create a new one in Settings → API keys.
API_ORDER_IDEMPOTENCY_CONFLICT
409
This Idempotency-Key was already used for different order data.
Use one key per order. Do not reuse a key for another order or for a changed order.
API_ORDER_EXTERNAL_ID_CONFLICT
409
Akeed already has an order with this externalOrderId, and its data is different.
Orders cannot be changed through the API. Open the order in the dashboard, or use a new externalOrderId if it really is a new order.
API_SOURCE_UNAVAILABLE
409
The store this key belongs to cannot take orders: it was deactivated or replaced. A store that is not a Standalone store answers the same code with status 403.
Sign in to Akeed and check the store. Contact support if it looks active.
API_SETUP_INCOMPLETE
409
The store has not finished setup.
Finish setup in the Akeed app, then send the order again with the same key.
API_AUTO_VERIFY_DISABLED
409
Automatic confirmation is turned off for the store.
Turn it on in Settings, then send the order again with the same key.
API_ENTITLEMENT_REQUIRED
409
The store has no active plan or billing.
Open Billing & credits in the Akeed app.
API_PLAN_LIMIT_REACHED
409
The confirmations included for this period are used up.
Open Billing & credits in the Akeed app.
INSUFFICIENT_CREDITS
409
The credit balance cannot cover one more message.
Buy credits, then send the order again with the same key.
CREDIT_ACCOUNT_SUSPENDED
409
The credit account is suspended.
Contact support.
CREDIT_DEBT_OUTSTANDING
409
The credit account has a negative balance to settle first.
Open Billing & credits and settle the balance.
CREDIT_ACCOUNT_NOT_PROVISIONED
409
The store has no credit account.
Contact support.
API_PAYLOAD_TOO_LARGE
413
The request body is larger than the limit.
Send one order per request and keep the text fields short.
API_RATE_LIMITED
429
Too many requests in one minute.
Wait the number of seconds in Retry-After, then send the request again with the same key.
API_INTERNAL_ERROR
500
Something failed on Akeed's side.
Retry with the same key. If it keeps failing, send support the correlationId.
API_ORDER_ACCEPTANCE_FAILED
503
The order could not be stored. Nothing was saved.
Retry with the same key.
API_ORDER_DISPATCH_FAILED
503
The order was stored, but its confirmation could not be queued.
Retry with the same key. The retry queues the same order and never creates a second one.
If you receive a 409 with a code that is not in this table, treat it the same way as the credit codes: the store cannot send right now, and nothing was stored.
Every response carries an X-Correlation-Id header, and every error repeats it as correlationId in the body. It identifies that one request in Akeed's logs. Log it on your side for every request that does not return 202.
Never send your API key, in full or in part, to anyone. Support does not need it. If a key may have been exposed, revoke it in Settings → API keys and create a new one: requests with the old key are refused at once, and orders it already sent are kept.
You can also set the header yourself. If your request has an X-Correlation-Id of 8 to 64 letters, numbers, dots, underscores or hyphens, Akeed uses your value, so one id follows the order through both systems.
It is a good habit. Name each key after the server that uses it, so you can revoke one without stopping the others. All keys of a store share the same request limit.