Povito for developers
Sync your catalog, stock, and prices with Povito, and pull your orders, straight from the ERP, POS, or inventory system you already run. Built for sellers whose stock lives in their own system and should keep living there.
Traced from the live API — every path, field, and limit on this page is read from the running implementation, not a spec that might be stale.
The Partner API is a machine-to-machine REST API. It is bulk-first and keyed entirely by your own SKUs, so your system never has to learn or store Povito’s internal product ids. It is a different surface from the seller dashboard’s own API: that one is built around a person clicking through screens, this one is built around a nightly job pushing five hundred stock levels.
Base URL
All money is a whole number of Iraqi dinars. There are no decimals and no minor units anywhere in this API — 25000 means 25,000 IQD. Every timestamp is ISO 8601 in UTC.
Confirm you can reach Povito before you create anything. This endpoint needs no key:
curl https://api.povito.com/partner/v1/healthThen create an API key in your seller dashboard under Settings → Integrations, and read your catalog back:
curl https://api.povito.com/partner/v1/products?limit=5 \
-H "Authorization: Bearer povito_sk_live_..."Then push stock and prices for the SKUs your system owns:
curl -X PATCH https://api.povito.com/partner/v1/inventory/bulk \
-H "Authorization: Bearer povito_sk_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "external_sku": "ERP-1042", "on_hand": 12, "price_iqd": 24000 }
]
}'Keys are created and revoked in the seller dashboard, not through this API. Open Settings → Integrations.
Every request except /health carries your key as a bearer token:
Authorization: Bearer povito_sk_live_a1b2c3d4e5f6_<secret>live / test segment reflects the Povito environment that issued the key. Keys issued from the production dashboard are povito_sk_live_.You choose a key’s scopes when you create it, and can change them later. Grant only what the integration actually needs — a nightly stock sync does not need to create products.
| Scope | Grants |
|---|---|
| catalog:read | Read your products and their variants, prices, and stock. |
| catalog:write | Create products and update their title, description, and categories. |
| inventory:write | Set stock levels and prices, and post stock adjustments. |
| orders:read | Read your orders and their line items. |
Calling an endpoint your key lacks the scope for returns 403 with code insufficient_scope, naming the scope it needed.
Connectivity check. The only endpoint that takes no API key — call it first to confirm you are reaching Povito before you wire a key in.
{
"status": "ok",
"api": "partner",
"version": "v1",
"currency": "IQD"
}Pull your current catalog state, newest first.
catalog:read| Query parameter | Type | Notes |
|---|---|---|
| status | string | Filter by product status, e.g. published, draft, proposed. |
| cursor | string | next_cursor from the previous page. Omit for the first page. |
| limit | integer | Rows per page. Defaults to 20, capped at 100. |
{
"data": [
{
"id": "prod_01J...",
"title": "Wireless Headphones",
"handle": "wireless-headphones",
"status": "published",
"description": "...",
"thumbnail": "https://.../image.jpg",
"images": ["https://.../image.jpg"],
"category_ids": ["pcat_01J..."],
"brand": { "id": "brand_01J...", "name": "Acme" },
"variants": [
{
"offer_id": "off_01J...",
"variant_id": "variant_01J...",
"sku": "ERP-1042",
"options": { "Color": "Black" },
"price": 25000,
"sale_price": null,
"campaign_label": null,
"stocked_quantity": 12,
"condition": null,
"notes": null
}
],
"catalog_managed": false,
"handling": null,
"rejection_reason": null,
"created_at": "2026-09-01T10:00:00.000Z",
"updated_at": "2026-09-04T08:12:00.000Z"
}
],
"next_cursor": "off_01J...",
"has_more": true
}Look up one product by your own SKU. You never need to store Povito product ids to use this API.
catalog:read{
"id": "prod_01J...",
"title": "Wireless Headphones",
"status": "published",
"variants": [ /* same shape as the list endpoint */ ],
"created_at": "2026-09-01T10:00:00.000Z",
"updated_at": "2026-09-04T08:12:00.000Z"
}Create or update products in one call, keyed by your own SKU.
catalog:writeIdempotency-Key header{
"items": [
{
"external_sku": "ERP-1042",
"title": "Wireless Headphones",
"price_iqd": 25000,
"description": "Optional.",
"category_ids": ["pcat_01J..."],
"initial_stock": 10
},
{ "external_sku": "ERP-1043", "title": "Existing SKU, renamed" }
]
}{
"results": [
{ "external_sku": "ERP-1042", "status": "accepted", "product_id": "prod_01J..." },
{
"external_sku": "ERP-1043",
"status": "rejected",
"error": {
"code": "validation_error",
"message": "title and price_iqd are required to create a new SKU."
}
}
]
}Set absolute stock levels and prices. This is the endpoint most ERPs call on a schedule.
inventory:writeIdempotency-Key header{
"items": [
{ "external_sku": "ERP-1042", "on_hand": 12, "price_iqd": 24000 },
{ "external_sku": "ERP-1043", "on_hand": 0 }
]
}{
"results": [
{ "external_sku": "ERP-1042", "status": "accepted" },
{ "external_sku": "ERP-1043", "status": "accepted" }
]
}Post a stock movement with a reason, for anything that changed outside Povito — a shop-floor sale, breakage, or a restock.
inventory:write{
"reason": "Counted during weekly stocktake",
"category": "damaged",
"items": [
{ "external_sku": "ERP-1042", "delta": -2 },
{ "external_sku": "ERP-1043", "delta": 5, "location_id": "sloc_01J..." }
]
}{
"reason": "Counted during weekly stocktake",
"category": "damaged",
"data": [ /* one row per adjusted variant */ ]
}Pull your orders, newest first.
orders:read| Query parameter | Type | Notes |
|---|---|---|
| since | ISO 8601 date-time | Only orders created at or after this instant. |
| cursor | string | next_cursor from the previous page. |
| limit | integer | Rows per page. Defaults to 50, capped at 200. |
{
"data": [
{
"order_id": "order_01J...",
"increment_id": "POV-41",
"status": "pending",
"total": 25000,
"currency": "IQD",
"created_at": "2026-09-04T08:12:00.000Z"
}
],
"next_cursor": "case_01J...",
"has_more": false
}One order in full, including line items and what you will be paid.
orders:read{
"order_id": "order_01J...",
"increment_id": "POV-41",
"status": "shipped",
"total": 25000,
"currency": "IQD",
"shipping_address": {
"first_name": "...",
"last_name": "...",
"address_1": "...",
"city": "Baghdad",
"phone": "+9647..."
},
"items": [
{
"item_id": "item_01J...",
"title": "Wireless Headphones",
"quantity": 1,
"unit_price": 25000,
"variant_id": "variant_01J...",
"external_sku": "ERP-1042"
}
],
"accepted_at": "2026-09-04T09:00:00.000Z",
"shipped_at": "2026-09-04T14:00:00.000Z",
"tracking_number": "...",
"carrier": "...",
"created_at": "2026-09-04T08:12:00.000Z",
"settlement": {
"status": "held",
"commission_percent": 8,
"commission_amount": 2000,
"earnings": 23000,
"hold_days": 7,
"hold_starts_at": "2026-09-04T14:00:00.000Z",
"funds_released_at": null,
"delivery_confirmation_source": null
}
}List endpoints are cursor-paginated and return newest-first. Keep requesting until has_more is false, passing the previous response’s next_cursor each time.
{
"data": [ /* ... */ ],
"next_cursor": "off_01J...",
"has_more": true
}Treat the cursor as opaque — do not parse it or construct one yourself. Its format is not part of this contract.
POST /products/bulk and PATCH /inventory/bulk require an Idempotency-Key header — any unique string per logical request, a UUID is ideal. A request without one is rejected.
Both endpoints are naturally safe to retry: SKUs are upserted, and stock and price are absolute values rather than deltas, so replaying the same body produces the same end state. The header is there so your retries are traceable on both sides — Povito does not currently replay a cached response for a repeated key, it simply runs the same upsert again.
Both bulk endpoints allow 60 requests per 10 minutes, per API key. Exceeding that returns 429 with a Retry-After header in seconds. Because the limit counts requests and not rows, batching properly — 500 stock rows in one call rather than 500 calls — is what keeps you inside it.
Every error shares one envelope:
{
"error": {
"code": "validation_error",
"message": "items cannot exceed 50 rows per request.",
"details": [{ "field": "items", "issue": "too_many" }],
"request_id": "req_01J..."
}
}Log request_id — it is what Povito support needs to trace a specific failed call.
| Status | When |
|---|---|
| 400 | The request itself is malformed — bad JSON, a missing required field, or a batch over the row cap. |
| 401 | Missing, invalid, or revoked API key (code invalid_api_key). A seller-dashboard login token will not work here. |
| 403 | Valid key, but it is missing the scope this endpoint needs (code insufficient_scope). |
| 404 | The resource does not exist, or the external_sku matched none of your products. |
| 422 | A business rule rejected the request, e.g. a price below the platform floor. |
| 429 | Rate limit exceeded. Read the Retry-After response header for how many seconds to wait. |
| 5xx | Retryable on Povito's side. Log the request_id and retry with backoff. |
One case to handle deliberately: the bulk endpoints return 200 even when individual rows fail. A top-level 4xx means the whole request was wrong; a rejected row inside results[] means that one SKU was. Treating “HTTP 200” as “everything synced” is the most common integration bug here.
There is no separate sandbox environment or shared test credential today. You test against the live API using your own key. Povito issues no shared demo keys — every key belongs to exactly one real seller account, which is what keeps one seller’s data unreachable from another’s integration.
That constraint is workable, because the destructive operations here are the ones you opt into by scope. A safe rollout looks like this:
catalog:read and orders:read only. Nothing you do with it can change a thing.inventory:write and try one row through PATCH /inventory/bulk on a single low-risk SKU. Read it back with GET /products/{external_sku} to confirm.catalog:write last. New products land as drafts and go through Povito’s normal review before any shopper sees them, so a malformed first import cannot publish anything by accident.If your integration genuinely needs an isolated environment before touching real inventory, talk to Povito — that is a real gap and worth raising rather than working around.
Stated plainly so you do not build around something that is not there:
GET /orders with since on a schedule.GET /products has no updated_since filter — catching up means re-pulling from the first page. GET /orders does support since.