ReferenceReference by module

Campaigns API

Every Campaigns action (16) and data model (7): endpoints, scopes, input, preconditions, events and examples.

Email, SMS and WhatsApp campaigns to your customers, with opens and clicks.

Module code campaigns · 16 actions · 7 data models · 12 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: campaigns (everything), campaigns:read (read only) or campaigns:<capability>.

Resources

ResourceTableList and readPOSTPATCHDELETE
audience_factsaudience_fact/api/v1/audience_facts
audiencesaudience/api/v1/audiencescampaigns.create_audiencecampaigns.update_audiencecampaigns.delete_audience
campaign_clickscampaign_click/api/v1/campaign_clicks
campaign_linkscampaign_link/api/v1/campaign_links
campaign_recipientscampaign_recipient/api/v1/campaign_recipients
campaignscampaign/api/v1/campaignscampaigns.createcampaigns.updatecampaigns.delete
marketing_unsubscribesmarketing_unsubscribe/api/v1/marketing_unsubscribes

Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.

Actions

ActionNeedsWhat it does
campaigns.cancelcampaigns:writeStop sending
campaigns.createcampaigns:writeDraft a campaign
campaigns.create_audiencecampaigns:writeCreate an audience
campaigns.deletecampaigns:deleteDelete a draft campaign
campaigns.delete_audiencecampaigns:deleteDelete an audience
campaigns.record_batchcampaigns:handleRecord a sent batch
campaigns.resubscribecampaigns:manageTake off the unsubscribe list
campaigns.resubscribe_mecampaigns:writeSend me campaign messages again
campaigns.schedulecampaigns:writeSchedule or send
campaigns.send_testcampaigns:writeSend a test
campaigns.startcampaigns:handleStart a due campaign
campaigns.unschedulecampaigns:writeBack to draft
campaigns.unsubscribecampaigns:writeUnsubscribe an address
campaigns.unsubscribe_mecampaigns:writeStop campaign messages to me
campaigns.updatecampaigns:writeEdit a campaign
campaigns.update_audiencecampaigns:writeEdit an audience

campaigns.cancel

Stop sending — Stops a scheduled or sending campaign; whoever has not been sent to yet is not.

Needs campaigns:write · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/cancel
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.cancel

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_cancellableOnly a scheduled or sending campaign is stopped.

Events it publishes: campaigns.cancelled

What it writes: campaign (update) — Marks it cancelled; campaign_recipient (update) — Skips those not yet sent to

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/cancel \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.cancelled"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.cancel("CAMPAIGN_ID")
era campaigns cancel CAMPAIGN_ID
tools/call
{
  "name": "era__campaigns__cancel",
  "arguments": {
    "id": "CAMPAIGN_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_cancellable · 429 slow down

campaigns.create

Draft a campaign — An email, SMS or WhatsApp message to an audience, drafted to be tested, scheduled and sent.

Needs campaigns:write · changes data

POSThttps://app.getera.io/api/v1/campaigns/actions/create
API key or OAuth token · campaigns:write
  • REST alias: POST /api/v1/campaigns
  • Canonical address: POST /api/v1/actions/campaigns.create

Input

FieldTypeRequiredNotes
namestringyesName · max 200
channelstring (enum)Send by · Allowed values: email, sms, whatsapp
formatstring (enum)Format · Allowed values: text, html
subjectstringSubject · max 300
subjectBstringSubject B (A/B test) · max 300
preheaderstringPreview line · max 200
bodystringMessage · max 20000
blocksobject / arrayEmail blocks
audienceIduuidyesAudience · id of audience

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
audience_not_foundThe audience must exist.
invalid_campaign_blocksThe email layout must be a list of known blocks with valid links.

Events it publishes: campaigns.created

What it writes: campaign (insert) — Writes the draft

curl -X POST https://app.getera.io/api/v1/campaigns/actions/create \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name","audienceId":"AUDIENCE_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.create({
  name: "Name",
  audienceId: "AUDIENCE_ID"
})
era campaigns create \
  --name "Name" \
  --audience-id "AUDIENCE_ID"
tools/call
{
  "name": "era__campaigns__create",
  "arguments": {
    "name": "Name",
    "audienceId": "AUDIENCE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 audience_not_found, invalid_campaign_blocks · 429 slow down

campaigns.create_audience

Create an audience — A saved segment of customers: their fields, their tags, whether they are a lead, and what they bought or booked.

Needs campaigns:write · changes data

POSThttps://app.getera.io/api/v1/audiences/actions/create_audience
API key or OAuth token · campaigns:write
  • REST alias: POST /api/v1/audiences
  • Canonical address: POST /api/v1/actions/campaigns.create_audience

Input

FieldTypeRequiredNotes
namestringyesName · max 200
descriptionstringDescription · max 2000
matchstring (enum)Match · Allowed values: all, any
rulesobject / arrayRules

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_audience_rulesEvery rule needs a known field, operator and value.

Events it publishes: campaigns.audience_changed

What it writes: audience (insert) — Saves the segment

curl -X POST https://app.getera.io/api/v1/audiences/actions/create_audience \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.audience_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.audiences.create({
  name: "Name"
})
era audiences create \
  --name "Name"
tools/call
{
  "name": "era__campaigns__create_audience",
  "arguments": {
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_audience_rules · 429 slow down

campaigns.delete

Delete a draft campaign — Removes a campaign that was never sent.

Needs campaigns:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/delete
API key or OAuth token · campaigns:delete
  • REST alias: DELETE /api/v1/campaigns/{id}
  • Canonical address: POST /api/v1/actions/campaigns.delete

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_draftOnly a draft is changed.

Events it publishes: campaigns.deleted

What it writes: campaign (delete) — Deletes the draft

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/delete \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.deleted"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.delete("CAMPAIGN_ID")
era campaigns delete CAMPAIGN_ID
tools/call
{
  "name": "era__campaigns__delete",
  "arguments": {
    "id": "CAMPAIGN_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_draft · 429 slow down

campaigns.delete_audience

Delete an audience — Removes a segment no campaign uses.

Needs campaigns:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/audiences/{id}/actions/delete_audience
API key or OAuth token · campaigns:delete
  • REST alias: DELETE /api/v1/audiences/{id}
  • Canonical address: POST /api/v1/actions/campaigns.delete_audience

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Audience · id of audience

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
audience_not_foundThe audience must exist.
audience_in_useAn audience a campaign uses stays.

Events it publishes: campaigns.audience_changed

What it writes: audience (delete) — Deletes the segment

curl -X POST https://app.getera.io/api/v1/audiences/AUDIENCE_ID/actions/delete_audience \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.audience_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.audiences.deleteAudience("AUDIENCE_ID")
era audiences delete_audience AUDIENCE_ID
tools/call
{
  "name": "era__campaigns__delete_audience",
  "arguments": {
    "id": "AUDIENCE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 audience_not_found, audience_in_use · 429 slow down

campaigns.record_batch

Record a sent batch — Machine-only (campaigns-jobs.ts): which recipients were handed to the sender and which were skipped.

Needs campaigns:handle · changes data

Run by Era itself (machine-only). Listed so the reference is complete; an API key or a token is refused (403 machine_only).
POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/record_batch
API key or OAuth token · campaigns:handle
  • Canonical address: POST /api/v1/actions/campaigns.record_batch

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign
entriesobject / arrayyesEntries

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
invalid_batchThe batch must be a list.

Events it publishes: campaigns.batch_recorded, campaigns.sent

What it writes: campaign_recipient (update) — Queued or skipped; campaign (update) — Sent when nobody is left

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, invalid_batch · 429 slow down

campaigns.resubscribe

Take off the unsubscribe list — For a person who asked to hear from you again.

Needs campaigns:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/marketing_unsubscribes/actions/resubscribe
API key or OAuth token · campaigns:manage
  • Canonical address: POST /api/v1/actions/campaigns.resubscribe

Input

FieldTypeRequiredNotes
emailstringyesEmail or phone · max 320

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
unsubscribe_not_foundThe address must be on the list.

Events it publishes: campaigns.resubscribed

What it writes: marketing_unsubscribe (delete) — Removes the address

curl -X POST https://app.getera.io/api/v1/marketing_unsubscribes/actions/resubscribe \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"email":"Email or phone"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.resubscribed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.marketingUnsubscribes.resubscribe({
  email: "Email or phone"
})
era marketing_unsubscribes resubscribe \
  --email "Email or phone"
tools/call
{
  "name": "era__campaigns__resubscribe",
  "arguments": {
    "email": "Email or phone"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unsubscribe_not_found · 429 slow down

campaigns.resubscribe_me

Send me campaign messages again — A signed-in customer turns campaign emails and texts back on from their site account.

Needs campaigns:write · removes or cannot be undone · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/marketing_unsubscribes/actions/resubscribe_me
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.resubscribe_me

Input

This action takes no input.

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
no_contact_on_recordThe customer’s record needs an email or a phone.
unsubscribe_not_foundOnly when they are on the list.

Events it publishes: campaigns.resubscribed

What it writes: marketing_unsubscribe (delete) — Their own address off the list

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_contact_on_record, unsubscribe_not_found · 429 slow down

campaigns.schedule

Schedule or send — Sends at the chosen time, or now; it goes out in batches through notifications.

Needs campaigns:write · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/schedule
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.schedule

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign
sendAtdate-timeSend at

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_draftOnly a draft is changed.
campaign_incompleteA message (and for email a subject) is needed; SMS is at most 1500 characters.
campaign_no_channelAn email campaign needs an active email channel.
audience_emptyThe audience must reach at least one address.

Events it publishes: campaigns.scheduled

What it writes: campaign (update) — Marks it scheduled

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/schedule \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"sendAt":"2026-11-02T09:00:00Z"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.scheduled"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.schedule("CAMPAIGN_ID", {
  sendAt: "2026-11-02T09:00:00Z"
})
era campaigns schedule CAMPAIGN_ID \
  --send-at "2026-11-02T09:00:00Z"
tools/call
{
  "name": "era__campaigns__schedule",
  "arguments": {
    "id": "CAMPAIGN_ID",
    "sendAt": "2026-11-02T09:00:00Z"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_draft, campaign_incomplete, campaign_no_channel, audience_empty · 429 slow down

campaigns.send_test

Send a test — One copy to one address, through the same email channel the campaign will use — the designed email as it goes out.

Needs campaigns:write · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/send_test
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.send_test

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign
tostringyesSend to · max 320
variantstring (enum)Subject · Allowed values: a, b

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
invalid_recipientThe address must be an email address.

Events it publishes: campaigns.test_requested

What it writes: campaign (update) — Stamps when a test last went

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/send_test \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"to":"Send to"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.test_requested"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.sendTest("CAMPAIGN_ID", {
  to: "Send to"
})
era campaigns send_test CAMPAIGN_ID \
  --to "Send to"
tools/call
{
  "name": "era__campaigns__send_test",
  "arguments": {
    "id": "CAMPAIGN_ID",
    "to": "Send to"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, invalid_recipient · 429 slow down

campaigns.start

Start a due campaign — Machine-only (campaigns-jobs.ts): lists the recipients, registers the links to track, and starts sending.

Needs campaigns:handle · changes data

Run by Era itself (machine-only). Listed so the reference is complete; an API key or a token is refused (403 machine_only).
POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/start
API key or OAuth token · campaigns:handle
  • Canonical address: POST /api/v1/actions/campaigns.start

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_dueOnly a scheduled campaign whose time came starts.

Events it publishes: campaigns.started, campaigns.sent

What it writes: campaign_recipient (insert) — One row per address; campaign_link (insert) — One row per link in the email, for click tracking; campaign (update) — Marks it sending

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_due · 429 slow down

campaigns.unschedule

Back to draft — Takes a scheduled campaign back to a draft before it starts.

Needs campaigns:write · changes data

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/unschedule
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.unschedule

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_scheduledOnly a scheduled campaign is unscheduled.

Events it publishes: campaigns.updated

What it writes: campaign (update) — Back to draft

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/unschedule \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.updated"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.unschedule("CAMPAIGN_ID")
era campaigns unschedule CAMPAIGN_ID
tools/call
{
  "name": "era__campaigns__unschedule",
  "arguments": {
    "id": "CAMPAIGN_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_scheduled · 429 slow down

campaigns.unsubscribe

Unsubscribe an address — Puts an address or phone number on the unsubscribe list — from the message’s link, or by a member on request.

Needs campaigns:write · changes data · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/marketing_unsubscribes/actions/unsubscribe
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.unsubscribe

Input

FieldTypeRequiredNotes
tokenstringLink token · max 100
emailstringEmail · max 320
phonestringPhone · max 40
reasonstringReason · max 1000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
unsubscribe_link_invalidA link token must be one this workspace sent.
unsubscribe_link_requiredFrom the site, only the link from the message unsubscribes.
invalid_recipientAn address must be an email address or a phone number.

Events it publishes: campaigns.unsubscribed

What it writes: marketing_unsubscribe (insert) — Adds the address to the list

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unsubscribe_link_invalid, unsubscribe_link_required, invalid_recipient · 429 slow down

campaigns.unsubscribe_me

Stop campaign messages to me — A signed-in customer turns off campaign emails and texts from their site account.

Needs campaigns:write · changes data · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/marketing_unsubscribes/actions/unsubscribe_me
API key or OAuth token · campaigns:write
  • Canonical address: POST /api/v1/actions/campaigns.unsubscribe_me

Input

This action takes no input.

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
no_contact_on_recordThe customer’s record needs an email or a phone.
already_unsubscribedNot when they are already off.

Events it publishes: campaigns.unsubscribed

What it writes: marketing_unsubscribe (insert) — Their own address on the list

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_contact_on_record, already_unsubscribed · 429 slow down

campaigns.update

Edit a campaign — Changes a draft’s name, channel, subject, message, email blocks or audience.

Needs campaigns:write · changes data

POSThttps://app.getera.io/api/v1/campaigns/{id}/actions/update
API key or OAuth token · campaigns:write
  • REST alias: PATCH /api/v1/campaigns/{id}
  • Canonical address: POST /api/v1/actions/campaigns.update

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Campaign · id of campaign
namestringName · max 200
channelstring (enum)Send by · Allowed values: email, sms, whatsapp
formatstring (enum)Format · Allowed values: text, html
subjectstringSubject · max 300
subjectBstringSubject B (A/B test) · max 300
preheaderstringPreview line · max 200
bodystringMessage · max 20000
blocksobject / arrayEmail blocks
audienceIduuidAudience · id of audience

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
campaign_not_foundThe campaign must exist.
campaign_not_draftOnly a draft is changed.
audience_not_foundThe audience must exist.
invalid_campaign_blocksThe email layout must be a list of known blocks with valid links.

Events it publishes: campaigns.updated

What it writes: campaign (update) — Rewrites the draft

curl -X POST https://app.getera.io/api/v1/campaigns/CAMPAIGN_ID/actions/update \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.updated"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.campaigns.update("CAMPAIGN_ID", {
  name: "Name"
})
era campaigns update CAMPAIGN_ID \
  --name "Name"
tools/call
{
  "name": "era__campaigns__update",
  "arguments": {
    "id": "CAMPAIGN_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 campaign_not_found, campaign_not_draft, audience_not_found, invalid_campaign_blocks · 429 slow down

campaigns.update_audience

Edit an audience — Changes the segment’s name or rules. Campaigns already sent keep who they were sent to.

Needs campaigns:write · changes data

POSThttps://app.getera.io/api/v1/audiences/{id}/actions/update_audience
API key or OAuth token · campaigns:write
  • REST alias: PATCH /api/v1/audiences/{id}
  • Canonical address: POST /api/v1/actions/campaigns.update_audience

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Audience · id of audience
namestringName · max 200
descriptionstringDescription · max 2000
matchstring (enum)Match · Allowed values: all, any
rulesobject / arrayRules

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
audience_not_foundThe audience must exist.
invalid_audience_rulesEvery rule needs a known field, operator and value.

Events it publishes: campaigns.audience_changed

What it writes: audience (update) — Rewrites the segment

curl -X POST https://app.getera.io/api/v1/audiences/AUDIENCE_ID/actions/update_audience \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "campaigns.audience_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.audiences.updateAudience("AUDIENCE_ID", {
  name: "Name"
})
era audiences update_audience AUDIENCE_ID \
  --name "Name"
tools/call
{
  "name": "era__campaigns__update_audience",
  "arguments": {
    "id": "AUDIENCE_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold campaigns:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 audience_not_found, invalid_audience_rules · 429 slow down

Data models

AudienceFact

Table audience_fact · Resource audience_facts

  • GET /api/v1/audience_facts
  • GET /api/v1/audience_facts/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
customer_idstring (uuid) · uuidcustomer
kindstring · textorder, visit, package
source_idstring · text
statestring · textopen, done, cancelled
occurred_onstring (date) · dateyes
active_untilstring (date) · dateyes
amountstring (int64) · int8
currencystring · bpcharyesmax 3
event_idstring (int64) · int8yes
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: workspace_id, kind, source_id

Checks:

  • CHECK ((amount >= 0))
  • CHECK (((currency IS NULL) OR (currency ~ '^[A-Z]{3}$'::text)))

Audience

Table audience · Resource audiences

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
descriptionstring · textyes
matchstring · textall, any
rulesobject · jsonb
created_by_user_idstring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK ((length(TRIM(BOTH FROM name)) > 0))
  • CHECK ((jsonb_typeof(rules) = 'array'::text))

CampaignClick

Table campaign_click · Resource campaign_clicks

  • GET /api/v1/campaign_clicks
  • GET /api/v1/campaign_clicks/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
campaign_idstring (uuid) · uuidcampaign
link_idstring (uuid) · uuidcampaign_link
recipient_idstring (uuid) · uuidyescampaign_recipient
occurred_atstring (date-time) · timestamptz

Primary key: id

Table campaign_link · Resource campaign_links

  • GET /api/v1/campaign_links
  • GET /api/v1/campaign_links/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
campaign_idstring (uuid) · uuidcampaign
urlstring · text
created_atstring (date-time) · timestamptz

Primary key: id

Unique: campaign_id, url

Checks:

  • CHECK ((url ~* '^(https?://\|mailto:\|tel:)'::text))

CampaignRecipient

Table campaign_recipient · Resource campaign_recipients

  • GET /api/v1/campaign_recipients
  • GET /api/v1/campaign_recipients/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
campaign_idstring (uuid) · uuidcampaign
customer_idstring (uuid) · uuidyescustomer
emailstring · textyes
namestring · textyes
statestring · textpending, queued, skipped
skip_reasonstring · textyes
delivery_idstring (uuid) · uuidyesnotification_delivery
queued_atstring (date-time) · timestamptzyes
created_atstring (date-time) · timestamptz
phonestring · textyes
variantstring · bpchara, b
opened_atstring (date-time) · timestamptzyes
open_countinteger · int4
clicked_atstring (date-time) · timestamptzyes
click_countinteger · int4

Primary key: id

Unique: campaign_id, email

Checks:

  • CHECK (((email IS NOT NULL) OR (phone IS NOT NULL)))

Campaign

Table campaign · Resource campaigns

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
subjectstring · text
bodystring · text
audience_idstring (uuid) · uuidaudience
statusstring · textdraft, scheduled, sending, sent, cancelled
scheduled_atstring (date-time) · timestamptzyes
started_atstring (date-time) · timestamptzyes
completed_atstring (date-time) · timestamptzyes
cancelled_atstring (date-time) · timestamptzyes
recipient_countinteger · int4
test_sent_atstring (date-time) · timestamptzyes
created_by_user_idstring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
channelstring · textemail, sms, whatsapp
formatstring · texttext, html
blocksobject · jsonb
preheaderstring · textyes
subject_bstring · textyes

Primary key: id

Checks:

  • CHECK ((jsonb_typeof(blocks) = 'array'::text))
  • CHECK ((length(TRIM(BOTH FROM name)) > 0))
  • CHECK (((status <> 'scheduled'::text) OR (scheduled_at IS NOT NULL)))

MarketingUnsubscribe

Table marketing_unsubscribe · Resource marketing_unsubscribes

  • GET /api/v1/marketing_unsubscribes
  • GET /api/v1/marketing_unsubscribes/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
emailstring · textyes
customer_idstring (uuid) · uuidyescustomer
campaign_idstring (uuid) · uuidyescampaign
sourcestring · textlink, manual
reasonstring · textyes
created_by_user_idstring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
phonestring · textyes

Primary key: id

Unique: workspace_id, email

Checks:

  • CHECK (((email IS NOT NULL) OR (phone IS NOT NULL)))
  • CHECK ((email = lower(email)))

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
campaigns.audience_changedAn audience was created, edited or deleted.id
campaigns.batch_recordedA batch of a campaign was handed to the sender.id, handled, pending
campaigns.cancelledA campaign was stopped.id, notSent, ownerUserId
campaigns.createdA campaign was drafted.id, ownerUserId
campaigns.deletedA draft campaign was deleted.id
campaigns.resubscribedAn address was taken off the unsubscribe list.email, customerId
campaigns.scheduledA campaign was scheduled.id, scheduledAt, audienceSize, ownerUserId
campaigns.sentEvery recipient of a campaign was handed to the sender.id, recipientCount, ownerUserId
campaigns.startedA campaign started sending.id, recipientCount
campaigns.test_requestedA test copy was asked for; notifications delivers it.id, to, subject, text, html, sourceId
campaigns.unsubscribedAn address unsubscribed.email, phone, customerId, campaignId, source
campaigns.updatedA draft campaign was edited or unscheduled.id
Was this page helpful?