Quickstart

End-to-end integration in ~10 minutes. The full reference for every endpoint lives at https://backend.brandrep.io/v1/docs.


What you need from your BrandRep contact

A few IDs are managed in the BrandRep dashboard, not via the API. Ask your BrandRep contact (the operator setting up your account) for these before you start:

You needWhyAPI-discoverable?
API keyAuthenticates every requestYou mint it yourself — see step 1
Brand IDsScopes campaignsYes — GET /v1/brands
Campaign IDsThe campaigns you'll drive sends forYes — GET /v1/campaigns?brand_id=...

The campaign itself is set up by your BrandRep operator in the dashboard. The DM body (and, if no sequence exists yet, the sequence + template) is supplied with each batch trigger via message_body — you don't need anything pre-configured on the campaign beyond an empty shell.

BrandRep decides which connected Instagram / LinkedIn account sends each batch — there's nothing for you to configure or pass. (Campaign creation and target upload are deferred to v1.1.)


1. Get your API key

Log into the BrandRep dashboard → sidebar → API Key.

The first time you open the page, your key is auto-issued and shown once. It looks like:

brk_live_a3f2c8d4e5f6g7h8i9j0k1l2m3n4o5p6

Copy it now. You won't see the full key again. If you lose it, click Revoke & Regenerate — the next page load mints a fresh one and shows it once.

The key is scoped to your BrandRep user account and grants access to every brand you own. There's only one active key per user — no key management UI to maintain.

Save it as an env var:

export BR_KEY="brk_live_..."

2. List your brands

The customer's engine should call this once on integration and cache the brand IDs.

curl -sH "Authorization: Bearer $BR_KEY" \
  https://backend.brandrep.io/v1/brands

Response:

{
  "data": [
    {
      "id": "0b8e1f1a-...",
      "object": "brand",
      "name": "Acme Cosmetics",
      "domain": "acme.com",
      "created_at": "2026-04-12T10:11:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
export BRAND="0b8e1f1a-..."

3. List campaigns in that brand

Campaigns are created via the BrandRep dashboard — not the API — so you're discovering, not creating.

curl -sH "Authorization: Bearer $BR_KEY" \
  "https://backend.brandrep.io/v1/campaigns?brand_id=$BRAND"

Pick a campaign that's in not_started or in_progress status and save its ID:

export CAMPAIGN="<campaign-uuid>"

4. Pull the leads you want to message

The not_sent=true filter is the most useful for the agent-style "give me the next slice to message" pattern. A 30k-lead campaign pulled a page at a time (max 200 per page) becomes:

curl -sH "Authorization: Bearer $BR_KEY" \
  "https://backend.brandrep.io/v1/campaigns/$CAMPAIGN/leads?not_sent=true&limit=200"

Save the IDs of the leads you want to include in this batch (e.g. LEAD_IDS=...).

Other useful filters (combine freely with AND):

  • state=<value> — exact-match on lead outreach state. Valid values:

    StateMeaning
    queuedIn a batch, not yet sent
    awaitingSent successfully, no reply yet
    repliedLead replied to your DM
    failedSend failed
    skippedDropped from a batch mid-run
    haltedAccount problem stopped this lead
    completedEnd of the sequence for this lead
  • has_replied=false — leads with no inbound reply yet

  • last_batch_id=<uuid> — leads that were part of a specific batch

  • sequence_step_id=<uuid> — leads currently on a specific sequence step

Pagination: pass next_cursor from the response as starting_after on the next call.


5. Trigger a batch

BrandRep picks which connected Instagram or LinkedIn account sends the batch — you don't choose or pass an account. Just select your leads and supply the message:

curl -sX POST \
  -H "Authorization: Bearer $BR_KEY" \
  -H "Content-Type: application/json" \
  "https://backend.brandrep.io/v1/campaigns/$CAMPAIGN/batches" \
  -d "{
    \"lead_ids\": [\"<lead-uuid-1>\", \"<lead-uuid-2>\"],
    \"message_body\": \"hey, loved your content — would love to collab!\"
  }"

Response (HTTP 202):

{
  "id": "<batch-uuid>",
  "object": "batch",
  "campaign_id": "...",
  "status": "queued",
  "total_count": 2,
  ...
}
export BATCH="<batch-uuid>"

Three accepted input shapes for lead selection (pass exactly one):

KeyTypeWhen to use
lead_idsUUIDs from /v1/campaigns/{id}/leadsMost direct
influencer_ids12-char nanoidsWhen you're tracking influencers, not leads
profile_urlsstrings (instagram.com / linkedin.com)When the customer-side data is keyed on URLs

message_body is required — the DM text that will be sent to every lead in this batch.

  • The first time you trigger a batch on a campaign, BrandRep auto-creates the underlying sequence + template using this body. You don't need the operator to pre-configure anything in the dashboard.
  • On every subsequent trigger, the body replaces the campaign's step-1 template body in place. Anything you send going forward (including future batches that don't supply a message_body of their own — but that's not allowed in v1; the field is always required) uses the most recent value.
  • Plain text only in v1, no placeholder substitution.

Optional: sequence_step_id. Campaigns can have multi-step sequences (follow-ups). The batch trigger defaults to step 1 if you omit sequence_step_id. message_body always becomes step 1's body — passing a non-step-1 sequence_step_id selects which step gets sent, not which step's body the message_body overwrites. Find existing step IDs from last_sequence_step_id on a LeadRead row, or ask your BrandRep contact.

The batch is queued — the operator-side worker picks it up and processes it asynchronously. You learn about progress via webhooks (preferred) or by polling GET /v1/batches/{id}.


6. Watch the batch progress

Option A — polling

curl -sH "Authorization: Bearer $BR_KEY" \
  "https://backend.brandrep.io/v1/batches/$BATCH"

Status transitions: queuedrunningcompleted (or failed / cancelled). Counters update live: sent_count, failed_count, skipped_count.

Pickup latency. Expect up to ~30 seconds between the POST /batches returning 202 and the batch transitioning to running — the operator-side worker polls for new batches on that interval. A missing batch.started event inside the first half-minute is normal cadence, not a failure.

Option B — webhooks (preferred)

Subscribe to batch.started, message.sent, message.failed, batch.completed. The engine becomes purely event-driven — no polling loop, no "how long until this finishes" guesses.

See webhooks.md for the full setup.


7. Tag your campaign and leads with your own IDs (recommended)

Every webhook envelope echoes back two customer-supplied IDs:

  • campaign_external_ref — your ID for the campaign (inside data on all events)
  • lead_external_ref — your ID for the lead (inside data on message events)

If you tag both, your webhook handler can route events purely on your own IDs without maintaining a brandrep_id → your_id lookup table.

Tag the campaign:

curl -sX PATCH \
  -H "Authorization: Bearer $BR_KEY" \
  -H "Content-Type: application/json" \
  "https://backend.brandrep.io/v1/campaigns/$CAMPAIGN" \
  -d '{"external_ref": "my-campaign-id-1234"}'

Bulk-tag the leads (up to 1,000 per call, repeat as needed):

curl -sX PATCH \
  -H "Authorization: Bearer $BR_KEY" \
  -H "Content-Type: application/json" \
  "https://backend.brandrep.io/v1/campaigns/$CAMPAIGN/leads/external_refs" \
  -d '{
    "refs": [
      {"lead_id": "<lead-uuid-1>", "external_ref": "your-row-id-1"},
      {"lead_id": "<lead-uuid-2>", "external_ref": "your-row-id-2"}
    ]
  }'

Both endpoints are idempotent — re-applying the same value is a no-op. Tag once after a lead is added to the campaign and you're done.

If you don't tag, webhook envelopes will have campaign_external_ref: null and lead_external_ref: null — your handler will need its own lookup table to correlate events back to your data model. Tagging is the cheaper option.


8. Re-check who hasn't been contacted yet

The same call from step 4 will now exclude the leads you just batched (their most recent outbound message is status='sent' once the platform confirms delivery):

curl -sH "Authorization: Bearer $BR_KEY" \
  "https://backend.brandrep.io/v1/campaigns/$CAMPAIGN/leads?not_sent=true&limit=200"

Loop: pull → batch → wait for webhooks → pull again.


What's next

Things to note up front

  • No Idempotency-Key in v1. Retry only on network errors. For 5xx, verify state with the matching GET before retrying.
  • Campaign creation and target upload stay in the dashboard. The API is for driving sends. Message body, however, is now supplied on every batch trigger via message_body — no need to pre-configure the template in the dashboard. See step 5.
  • Webhook receivers MUST dedupe by BrandRep-Event-Id for ≥7 days. At-least-once delivery means the same event may arrive twice. See webhooks.md for the full contract.
  • Tag your IDs early. external_ref fields are null until you set them. Hit PATCH /v1/campaigns/{id} and PATCH /v1/campaigns/{id}/leads/external_refs right after the campaign is configured, so every webhook from then on carries your IDs. See step 7.