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}:
| Group | Keys |
|---|---|
| Pixels & analytics | metaPixel, tiktokPixel (legacy alias tikTokPixel), googleAnalytics, googleTags, clarity |
| Marketing | meta — see Marketing |
| Delivery carriers | yalidine, ecotrack, zrexpress, procolis, noest, maystroDelivery, zimou, mdmExpress, ecomanager, codpilot, feeefDelivery, orderdz — see Delivery |
| Data & automation | googleSheet, webhooks, connectors, ai, sms, telegram, dispatcher, customFields |
| Modules | inventory, finance, payment, security |
| Free-form | metadata |
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.
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/stores/{id}/integrations | All configs + effective-active map | Bearer + scope store.integrations |
GET | /v1/stores/{id}/integrations/{key} | One config + effectiveActive | Bearer + scope store.integrations |
PATCH | /v1/stores/{id}/integrations/{key} | Deep-merge into one config | Bearer + scope store.integrations |
PUT | /v1/stores/{id}/integrations/{key} | Replace one config | Bearer + scope store.integrations |
DELETE | /v1/stores/{id}/integrations/{key} | Remove one config | Bearer + 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 anoauth2Connectedboolean. Writes cannot set it either — only the OAuth callback can.webhooks.webhooks[].secretis replaced byhasSecret. Secrets are revealed exactly once on create/rotate through the dedicated webhooks API.
Redacted fields survive a GET → PUT round-trip: omitting them means "keep the stored value".
Write semantics
PATCHdeep-merges one level deep per key; nested arrays are replaced wholesale. The merged config is validated, not the patch in isolation — soPATCH {"active": true}on an already-configured integration succeeds, while enabling a half-configured one fails with422(e.g.clarityrequirestrackingCodewhenactive: true).PUTvalidates the body as the full config. Server-managed credentials are re-attached from the stored row.DELETEremoves the key. Deletingwebhooksalso 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).
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/stores/{storeId}/integrations/subscription/catalog | Global pricing catalog + billing rules | Public |
GET | /v1/stores/{storeId}/integrations/subscription/status | Store's subscription entries + effectiveActive / includedInPlan per id | Bearer |
POST | /v1/stores/{storeId}/integrations/subscription/subscribe | Subscribe (charges wallet when priced) | Bearer |
POST | /v1/stores/{storeId}/integrations/subscription/cancel | Turn off auto-renew; access kept until expiry | Bearer |
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:
ultraincludes every integration; other plans include the items listing that plan in their catalogincludedInPlans. Catalog items withprice: 0(or no catalog entry) are free. - Coupled pair:
financeandinventoryare 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
active→grace→past_duewith exponential backoff retries, and tocanceledafter the max failed window. Lapsed entries makeeffectiveActivereportfalse. - Insufficient balance returns
402with 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.