Menu

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

NameURL
Productionhttps://api.wamia.tn/connect/graphql
Sandboxhttps://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
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 } }"}'
GraphQL
query FirstCall {
  apiMyListings(limit: 5) {
    items { sku state priceMillimes availableQty buyBox updatedAt }
    nextCursor
  }
}

Request headers

HeaderValue
AuthorizationBearer wamia_… (or x-api-key: wamia_…)
User-AgentWamiaConnect/<app> <semver> (<platform details>) — connectors; free for custom integrations
Wamia-Idempotency-KeyUUID or any 8–128 character string, mutations only
Wamia-Dry-Runtrue — validate and report, write nothing
Content-Typeapplication/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.

CodeHTTPMeaning
UNAUTHENTICATED401Missing or unknown key.
KEY_EXPIRED401Past expires_at; rotate or reconnect.
CONNECTION_REVOKED401The shop disconnected the app.
CONNECTOR_UPGRADE_REQUIRED426Plugin version below the minimum; extensions.minSupportedVersion says which.
CONNECTOR_SUSPENDED403Wamia paused this connector (kill switch).
FORBIDDEN_SCOPE403The key lacks the scope the operation needs (see Scopes and limits).
TENANT_MISMATCH403The resource belongs to another store.
RATE_LIMITED429Bucket empty; wait extensions.retryAfterSeconds.
IDEMPOTENCY_CONFLICT409Same key, different payload.
BATCH_HELD409A circuit breaker held the batch (extensions.pendingBatchId); the shop confirms or discards it.
BATCH_TOO_LARGE422More items than the bulk limit (extensions.limit).
VALIDATION_FAILED422Per-field problems in extensions.fields[] { field, message }.
MEDIA_FETCH_REFUSED422Image URL not fetchable (private network, size, type).
NOT_FOUND404Unknown 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.

response
{
  "data": { "apiMyListings": { "items": [ … ], "nextCursor": null } },
  "extensions": {
    "rateLimit": { "kind": "read", "limit": 600, "remaining": 599 }
  }
}

Where next