Skip to navigation

Getting started

View as Markdown

Base URL

Every call goes to your store’s own address, over HTTPS:

https://www.your-store.co.il/api/v1/...

Your store’s domain, or its yoursite.coi.co.il address - both reach the same store. A key only works on its own store.

API keys

Create keys in the admin: חנות > API ומפתחות (the Super plan and up).

  • Shown once. The full key (coi_sk_...) is shown when it is created and never again - Coi keeps only a hash of it. Copy it into your system right away. Lost it? Delete it and create a new one.
  • Permissions per area. For products, orders, registered customers and leads a key gets no access, read, or read and update. Give each system only what it needs.
  • Expiry. No limit, 30 days, 90 days or a year.
  • Up to 5 active keys per store - one per connected system, so each can be deleted on its own.
  • The store’s owner gets an email whenever a key is created or deleted.

Send the key in a header on every call:

curl https://www.your-store.co.il/api/v1/orders?limit=10 \
-H "Authorization: Bearer coi_sk_..."

X-Api-Key: coi_sk_... works too. Never put the key in the URL - URLs end up in logs. A key sent as ?apikey= is refused with 400. Keep keys on your server, never in a web page: the API does not answer browsers from other sites (no CORS).

Requests and answers

Bodies are JSON (Content-Type: application/json), in and out. Updates use PATCH and change only the fields you send.

A successful answer:

{ "ok": true, "data": { "id": 1647, "name": "..." } }

An error, with a real HTTP status:

{ "ok": false, "error": { "code": "invalid_field", "message": "price must be a number." } }
StatusWhen
400A field or parameter is wrong (invalid_field, invalid_parameter, invalid_json, nothing_to_update)
401Missing, invalid, deleted or expired key (unauthorized) - always the same answer
403The key lacks the permission (insufficient_scope), or the plan does not include the API (plan_required)
404No such row in this store, or no such endpoint (not_found)
405Wrong method for this endpoint; the Allow header lists the right ones
409A limit or conflict: plan_limit, email_taken, registration_disabled
415The body is not JSON (json_required)
429Too many requests (rate_limited), or too many failed keys from one IP (too_many_failures)

Branch on error.code, not on the message.

Formats. Money is a number with 2 decimals. Dates are ISO 8601 with the offset, in Israel time: 2026-09-27T21:40:00+03:00. Ids are whole numbers.

Pagination

Lists come newest first, 50 per page (limit=1..100). Each page says whether there is more:

{ "ok": true, "data": [ ... ], "hasMore": true, "nextCursor": "aToxNjQ2" }

For the next page, pass nextCursor back as cursor, as is: GET /orders?limit=100&cursor=aToxNjQ2. Stop when hasMore is false. A page never skips or repeats rows when new orders arrive in between.

To sync only what changed, use the date filters: updated_since on products, created_since on orders, customers and leads.

Rate limit

Up to 120 requests a minute per key. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds to the next minute). Over the limit you get 429 with Retry-After. Updating many products? Use one bulk call of up to 500 items instead of 500 calls.

Safe retries

A POST that creates something (a product, a customer) takes an Idempotency-Key header - any unique string, such as your own record’s id. If the connection drops and you send the same request with the same key again within 24 hours, you get the first answer back (header Idempotent-Replayed: true) instead of a duplicate.

Moving from the old product endpoint

The old /api/product/update?apikey=... endpoint and the single per-store key are gone. To move:

  1. Create a key with products: read and update in API ומפתחות.
  2. Send it in the Authorization header.
  3. Update one product with PATCH /api/v1/products/{id}, or many at once with POST /api/v1/products/batch - it finds products by id, internalSku or sku, like the old endpoint did.