Developers
Build on PromReach
A REST API for your campaigns, content, analytics and orders, and signed webhooks that tell your tools when something happens. Connect Zapier, Make, Slack or your own code.
Authentication
Create a key in Settings → Integrations (Agency plan). A key belongs to one workspace and only has the scopes you give it. Send it as a bearer token; it's shown once, so store it safely.
curl https://api.promreach.com/api/v1/campaigns \
-H "Authorization: Bearer pz_live_…"Scopes
| Scope | Allows | For example |
|---|---|---|
| campaigns.read | Read campaigns and their results. | GET /api/v1/campaigns, GET /api/v1/campaigns/{id} |
| campaigns.manage | Create, launch, pause and edit campaigns. | POST /api/v1/campaigns, POST /api/v1/campaigns/{id}/pause |
| content.read | Read posts and the content calendar. | GET /api/v1/content, GET /api/v1/content/calendar |
| content.manage | Create, schedule and publish posts. | POST /api/v1/content, POST /api/v1/content/{id}/schedule |
| analytics.read | Read analytics, dashboards and reports. | GET /api/v1/analytics/overview, GET /api/v1/dashboards/{id}/data |
| orders.read | Read service orders. | GET /api/v1/orders |
| orders.manage | Place and manage service orders. | POST /api/v1/orders |
| wallet.read | Read the wallet balance and transactions. | GET /api/v1/wallet, GET /api/v1/wallet/transactions |
| wallet.manage | Spend from the wallet (deposits and payments). | POST /api/v1/wallet/deposits |
| creators.manage | Invite creators and manage collaborations. | POST /api/v1/creator-campaigns |
| social.manage | Connect and manage social accounts. | GET /api/v1/social-accounts |
Requests and errors
- JSON in and out. Successful responses are { success: true, data }; lists add meta with page, pageSize, total and totalPages.
- Page through lists with ?page=2&limit=50 (up to 100).
- Errors are { success: false, error: { code, message, fields } } with the HTTP status: 401 bad key, 403 missing scope, 404 not found, 402 plan limit, 409 conflict, 422 validation (see fields), 429 too many requests (wait for Retry-After).
- Money is a decimal string or number in the workspace currency, with the currency code next to it.
Webhooks
Add an endpoint in Settings → Integrations and choose events. We POST each event as JSON and retry with backoff if your endpoint doesn't answer with 2xx. Check the signature before you trust a delivery:
POST /your-endpoint
PromReach-Event: content.failed
PromReach-Delivery: 9a1e… (unique; use it to ignore repeats)
PromReach-Signature: t=1728000000,v1=<hex HMAC-SHA256 of "<t>.<raw body>" with your secret>
{ "id": "9a1e…", "type": "content.failed", "createdAt": "2026-10-04T08:00:00Z",
"organizationId": "…", "data": { "postId": "…", "platform": "facebook", "error": "Reconnect your Page." } }Node.js
import crypto from "node:crypto";
// header: PromReach-Signature: t=1728000000,v1=5257a8…
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = f"{parts['t']}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts["v1"])Event catalogue
campaign.launched
A campaign went live on the ad platform.
{ "data": { "campaignId": "3f6c…", "name": "Eid sale", "platform": "facebook" } }campaign.completed
A campaign reached its end date or budget.
{ "data": { "campaignId": "3f6c…", "name": "Eid sale" } }campaign.failed
The ad platform rejected or stopped a campaign.
{ "data": { "campaignId": "3f6c…", "error": "Ad account is disabled." } }content.published
A scheduled post was published.
{ "data": { "platforms": [ "instagram" ], "postId": "9a1e…" } }content.failed
A post couldn't be published on a platform.
{ "data": { "error": "Reconnect your Page.", "platform": "facebook", "postId": "9a1e…" } }content.review_requested
A post is waiting for approval.
{ "data": { "postId": "9a1e…" } }content.approved
A post was approved.
{ "data": { "postId": "9a1e…" } }content.changes_requested
A reviewer asked for changes to a post.
{ "data": { "note": "Use the new photo.", "postId": "9a1e…" } }inbox.new_messages
New comments or messages arrived in the inbox.
{ "data": { "count": 3, "threadIds": [ "c41d…" ] } }inbox.lead_captured
Someone finished a WhatsApp or Messenger lead form.
{ "data": { "answers": { "email": "rina@example.com", "name": "Rina" }, "channel": "whatsapp", "leadId": "5e0a…" } }insights.weekly_digest
The weekly AI digest is ready.
{ "data": { "digestId": "77b0…", "periodStart": "2026-09-28" } }insights.anomaly
A metric moved unusually (for example, a drop in reach).
{ "data": { "change": -42.5, "metric": "reach" } }agent.plan_ready
The AI agent drafted a plan to review.
{ "data": { "goalId": "5c2a…" } }agent.plan_failed
The AI agent couldn't draft a plan.
{ "data": { "goalId": "5c2a…" } }agent.goal_completed
An agent goal reached its target or end date.
{ "data": { "goalId": "5c2a…" } }listening.crisis
Negative mentions of a monitored term are spiking.
{ "data": { "alertId": "1b7c…", "monitorId": "e09f…" } }ads.boost_live
A boosted post is running.
{ "data": { "adAccountId": "0c5e…", "boostId": "aa31…" } }ads.boost_failed
Boosting a post didn't work.
{ "data": { "adAccountId": "0c5e…", "boostId": "aa31…" } }order.status_changed
A service order changed status.
{ "data": { "orderId": "d2f8…", "status": "in_progress" } }payment.succeeded
A wallet deposit or payment succeeded.
{ "data": { "amount": "50.00", "currency": "USD", "paymentId": "61aa…" } }payment.failed
A payment failed.
{ "data": { "paymentId": "61aa…" } }wallet.transaction
Money moved in or out of the wallet.
{ "data": { "amount": "50.00", "transactionId": "f3d2…", "type": "deposit" } }creator.application
A creator applied to an open brief.
{ "data": { "applicationId": "2c90…", "briefId": "8e3b…" } }creator.submission
A creator submitted a deliverable.
{ "data": { "collaborationId": "4d1f…" } }creator.response
A creator accepted or declined an invitation.
{ "data": { "collaborationId": "4d1f…", "status": "accepted" } }creator.completed
A creator collaboration was completed and paid.
{ "data": { "collaborationId": "4d1f…" } }creator.contract_signed
A creator signed a collaboration contract.
{ "data": { "collaborationId": "4d1f…", "contractId": "71ce…" } }subscription.renewed
The subscription renewed.
{ "data": { "plan": "pro" } }subscription.changed
The plan changed.
{ "data": { "from": "starter", "to": "pro" } }subscription.payment_failed
A subscription payment failed.
{ "data": { "invoiceId": "b8d1…" } }subscription.addon_expired
An add-on couldn't renew and ended.
{ "data": { "addonId": "c0f4…", "kind": "team_seats" } }
Zapier, Make and Slack
Zapier
- Trigger: Webhooks by Zapier → Catch Hook; copy its URL.
- Add it as a PromReach webhook and pick events.
- Actions back into PromReach: Webhooks by Zapier → Custom Request with your API key.
Make
- Start a scenario with Webhooks → Custom webhook; copy the address.
- Add it as a PromReach webhook.
- Call the API with the HTTP → Make a request module and a bearer token.
Slack
No code needed: connect a channel in Settings → Integrations to get approvals, failed posts and new inbox messages there.
