.dev
REST API

Integrations

Per-store integration configs, the config envelope, and paid integration subscriptions with wallet billing.

Every store carries a JSON map of integration configs — store.integrations — one entry per integration key (pixels, delivery carriers, order export, webhooks, security, modules…). The endpoints on this page edit one integration at a time (instead of PATCHing the whole store) and manage per-integration subscriptions for paid integrations.

Request/response conventions (errors, pagination, batch) are covered in API conventions.

Integration keys

Keys addressable via /v1/stores/{id}/integrations/{key}:

GroupKeys
Pixels & analyticsmetaPixel, tiktokPixel (legacy alias tikTokPixel), googleAnalytics, googleTags, clarity
Marketingmeta — see Marketing
Delivery carriersyalidine, ecotrack, zrexpress, procolis, noest, maystroDelivery, zimou, mdmExpress, ecomanager, codpilot, feeefDelivery, orderdz — see Delivery
Data & automationgoogleSheet, webhooks, connectors, ai, sms, telegram, dispatcher, customFields
Modulesinventory, finance, payment, security
Free-formmetadata

An unknown key returns 400 with Unknown integration key: {key}.

Config API — at a glance

All five routes require a bearer token with the store.integrations scope. Reads additionally need any member role on the store; writes need an editor/admin/owner role.

MethodPathDescriptionAuth
GET/v1/stores/{id}/integrationsAll configs + effective-active mapBearer + scope store.integrations
GET/v1/stores/{id}/integrations/{key}One config + effectiveActiveBearer + scope store.integrations
PATCH/v1/stores/{id}/integrations/{key}Deep-merge into one configBearer + scope store.integrations
PUT/v1/stores/{id}/integrations/{key}Replace one configBearer + scope store.integrations
DELETE/v1/stores/{id}/integrations/{key}Remove one configBearer + scope store.integrations

The config envelope

Single-key reads return the stored config plus effectiveActive — whether the integration is actually usable right now (active: true in the config and the store is entitled via plan, free catalog entry, or paid subscription):

{
  "storeId": "clx0a1b2c3",
  "key": "yalidine",
  "integration": { "id": "12345", "token": "…", "agent": "yalidine", "active": true },
  "effectiveActive": true
}

GET …/integrations returns the same idea for the whole map: { storeId, integrations, effectiveActive } where effectiveActive is a per-key boolean map. A lapsed subscription flips effectiveActive to false everywhere without touching the stored config.

Two fields are server-managed and redacted on every read:

  • meta.oauth2 (encrypted Meta access token) is replaced by an oauth2Connected boolean. Writes cannot set it either — only the OAuth callback can.
  • webhooks.webhooks[].secret is replaced by hasSecret. Secrets are revealed exactly once on create/rotate through the dedicated webhooks API.

Redacted fields survive a GETPUT round-trip: omitting them means "keep the stored value".

Write semantics

  • PATCH deep-merges one level deep per key; nested arrays are replaced wholesale. The merged config is validated, not the patch in isolation — so PATCH {"active": true} on an already-configured integration succeeds, while enabling a half-configured one fails with 422 (e.g. clarity requires trackingCode when active: true).
  • PUT validates the body as the full config. Server-managed credentials are re-attached from the stored row.
  • DELETE removes the key. Deleting webhooks also drops the store's webhook delivery history.
  • Writes are applied under a row lock, then the store cache is busted.

Enabling a paid integration without entitlement fails with 402:

{
  "message": "Subscribe to \"Finance\" (1500 DZD/mo) before enabling it.",
  "integrationId": "finance",
  "requiresSubscription": true
}

Read and update a config

# Read one integration config
curl -H "Authorization: Bearer $FEEEF_TOKEN" \
  https://api.feeef.org/v1/stores/{storeId}/integrations/clarity

# Enable it (deep-merge)
curl -X PATCH "https://api.feeef.org/v1/stores/{storeId}/integrations/clarity" \
  -H "Authorization: Bearer $FEEEF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "trackingCode": "abcd1234", "active": true }'

Integration subscriptions

Paid integrations are billed per store, per integration, independent from the store plan. Charges debit the store owner's wallet once per 30-day period (idempotent per store + integration + month).

MethodPathDescriptionAuth
GET/v1/stores/{storeId}/integrations/subscription/catalogGlobal pricing catalog + billing rulesPublic
GET/v1/stores/{storeId}/integrations/subscription/statusStore's subscription entries + effectiveActive / includedInPlan per idBearer
POST/v1/stores/{storeId}/integrations/subscription/subscribeSubscribe (charges wallet when priced)Bearer
POST/v1/stores/{storeId}/integrations/subscription/cancelTurn off auto-renew; access kept until expiryBearer

Subscribe/cancel are restricted to the store owner or a member with the integrations.billing permission — read-only roles can never spend the owner's wallet.

Rules worth knowing:

  • Plans: ultra includes every integration; other plans include the items listing that plan in their catalog includedInPlans. Catalog items with price: 0 (or no catalog entry) are free.
  • Coupled pair: finance and inventory are subscribed and canceled together, and are project-scoped — a second store on the same project inherits the sibling's entitlement without a duplicate charge.
  • Delivery carriers need credentials first: subscribing to a carrier never creates an { "active": true } stub — configure credentials via the config API, then enable.
  • Dunning: a failed renewal moves the entry activegracepast_due with exponential backoff retries, and to canceled after the max failed window. Lapsed entries make effectiveActive report false.
  • Insufficient balance returns 402 with a human-readable message.

Subscribe to a paid integration

curl -X POST "https://api.feeef.org/v1/stores/{storeId}/integrations/subscription/subscribe" \
  -H "Authorization: Bearer $FEEEF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "integrationId": "finance" }'

A successful subscribe reports how entitlement was granted:

{
  "message": "Subscribed successfully",
  "sharedProject": false,
  "includedInPlan": false,
  "free": false,
  "integrationIds": ["inventory", "finance"],
  "integrations": { "inventory": { "status": "active", "expiresAt": "…" }, "finance": { "…": "…" } },
  "effectiveActive": true
}

Cancel keeps access until the paid period ends — it only sets autoRenew: false.

Outbound webhooks

Order and product event webhooks (HMAC-signed pushes to your endpoint) live under the same base path (…/integrations/webhooks) but have a dedicated CRUD + test + delivery-history API. See Webhooks for events (orderCreated, productUpdated, …), payloads and signature verification.

On this page