Getting started with Wamia Connect
Wamia Connect is one GraphQL endpoint plus signed webhooks. A shop lists products, keeps prices and stock in sync, receives Wamia orders and ships them — from its own platform or from a custom integration. Every product still goes through the Wamia approval pipeline, exactly as if it had been entered by hand.
1. Get a key
A shop creates a custom key in Wamia Shop under Settings › API, choosing the scopes it carries. The key is shown once; store it encrypted. Connectors never ask for keys: they use the Connect flow (OAuth-style with PKCE) or an eight-character pairing code and receive a key bound to the shop.
Keys expire after 365 days. Rotate before expiry with apiRotateKey; the previous key keeps working for 24 hours.
2. Pick the endpoint
Production answers on https://api.wamia.tn/connect/graphql, the sandbox on https://api-dev.wamia.tn/connect/graphql. Both expose the same schema; the sandbox adds createSandboxSeller so you can open a test store without talking to anyone.
3. Send the headers every call needs
Authentication is Authorization: Bearer wamia_… or x-api-key. Connectors identify themselves with User-Agent: WamiaConnect/<app> <semver> (<platform details>): the API reads the version from it, refuses connectors below the minimum supported version with CONNECTOR_UPGRADE_REQUIRED, and apiConnectorLatest compares it with the newest release. A custom integration sends whatever user agent it likes. Mutations carry Wamia-Idempotency-Key; the same key with the same payload returns the first answer, a different payload returns IDEMPOTENCY_CONFLICT.
Endpoint
| Name | URL |
|---|---|
| Production | https://api.wamia.tn/connect/graphql |
| Sandbox | https://api-dev.wamia.tn/connect/graphql |
Your first call
List the five most recently updated listings of the key’s store. state tells you where each one stands in the approval pipeline and buyBox whether the offer currently wins.
curl -sS https://api.wamia.tn/connect/graphql \
-H 'content-type: application/json' \
-H 'x-api-key: wamia_…' \
-H 'user-agent: my-integration/1.0.0 (+https://shop.example.tn)' \
-H 'wamia-idempotency-key: 6f0e1a2b-…' \
--data '{"query":"query FirstCall { apiMyListings(limit: 5) { items { sku state priceMillimes availableQty buyBox updatedAt } nextCursor } }"}'query FirstCall {
apiMyListings(limit: 5) {
items { sku state priceMillimes availableQty buyBox updatedAt }
nextCursor
}
}Request headers
| Header | Value |
|---|---|
| Authorization | Bearer wamia_… (or x-api-key: wamia_…) |
| User-Agent | WamiaConnect/<app> <semver> (<platform details>) — connectors; free for custom integrations |
| Wamia-Idempotency-Key | UUID or any 8–128 character string, mutations only |
| Wamia-Dry-Run | true — validate and report, write nothing |
| Content-Type | application/json |
Errors
On /connect/graphql every refusal is a GraphQL error (HTTP 200) with extensions.code and, where it helps, machine-readable details next to it (extensions.retryAfterSeconds, extensions.fields, extensions.pendingBatchId, …); the HTTP column is what the REST endpoints (/connect/token, /connect/pair, /connect/revoke) answer. Arguments the schema validator rejects come back as BAD_USER_INPUT. Treat RATE_LIMITED and BATCH_HELD as normal flow, not failures.
| Code | HTTP | Meaning |
|---|---|---|
| UNAUTHENTICATED | 401 | Missing or unknown key. |
| KEY_EXPIRED | 401 | Past expires_at; rotate or reconnect. |
| CONNECTION_REVOKED | 401 | The shop disconnected the app. |
| CONNECTOR_UPGRADE_REQUIRED | 426 | Plugin version below the minimum; extensions.minSupportedVersion says which. |
| CONNECTOR_SUSPENDED | 403 | Wamia paused this connector (kill switch). |
| FORBIDDEN_SCOPE | 403 | The key lacks the scope the operation needs (see Scopes and limits). |
| TENANT_MISMATCH | 403 | The resource belongs to another store. |
| RATE_LIMITED | 429 | Bucket empty; wait extensions.retryAfterSeconds. |
| IDEMPOTENCY_CONFLICT | 409 | Same key, different payload. |
| BATCH_HELD | 409 | A circuit breaker held the batch (extensions.pendingBatchId); the shop confirms or discards it. |
| BATCH_TOO_LARGE | 422 | More items than the bulk limit (extensions.limit). |
| VALIDATION_FAILED | 422 | Per-field problems in extensions.fields[] { field, message }. |
| MEDIA_FETCH_REFUSED | 422 | Image URL not fetchable (private network, size, type). |
| NOT_FOUND | 404 | Unknown SKU, order or endpoint. |
Rate limits in every answer
Each response carries extensions.rateLimit { kind, limit, remaining } for the bucket the call consumed, mirrored in the X-RateLimit-Limit and X-RateLimit-Remaining headers; a 429 adds Retry-After. Errors carry extensions.requestId to quote in support tickets. Back off when remaining nears zero instead of waiting for 429.
{
"data": { "apiMyListings": { "items": [ … ], "nextCursor": null } },
"extensions": {
"rateLimit": { "kind": "read", "limit": 600, "remaining": 599 }
}
}Where next
- Connect flowPKCE authorisation, pairing codes and key lifecycle for connectors.
- ConceptsMatch-or-propose, listing states, guardrails, dry-run and cursors.
- Inbound and withdrawalsTop-ups by appointment, withdrawals and the warehouse count for stocked offers.
- API referenceEvery operation of
/connect/graphql, generated from the schema. - SandboxOpen a test store with a key and a pairing code in one step.