Skip to content
Documentation for: Echo
HUuptools.io
Markdown

Subscribers

Reading needs Subscribers: view, writing Subscribers: manage (Authentication).

{
"id": "d5f0b4b1-b59d-40eb-b2e0-74857893a055",
"email": "[email protected]",
"status": "unconfirmed",
"source": "api",
"fields": {"city": "Szeged", "last_name": "Farkas", "name": "Lilla", "rendelesek_szama": 3},
"groups": [{"id": "257e83a2-d55e-4a8c-b9a4-b3e939c151cc", "name": "Webshop vásárlók"}],
"signup_ip": "203.0.113.25",
"optin_ip": null,
"subscribed_at": "2026-10-22T08:52:08.527488Z",
"opted_in_at": null,
"unsubscribed_at": null,
"timezone": null,
"location_country": null,
"location_city": null,
"sent": 0,
"opens": 0,
"clicks": 0,
"open_rate": 0,
"click_rate": 0,
"created_at": "2026-10-22T08:52:08.527488Z",
"updated_at": "2026-10-22T08:52:08.527488Z"
}
Key Meaning
status active, unsubscribed, unconfirmed (double opt-in pending), bounced or junk (spam complaint).
source How the subscriber was added: manual, import, api, form, landing_page, integration or preference_center.
fields Values by field key (fields). Number fields are numbers, date fields YYYY-MM-DD strings. Empty fields are left out.
groups The groups the subscriber belongs to.
signup_ip, optin_ip The sign-up and the confirmation IP address, if known.
sent, opens, clicks, open_rate, click_rate Email statistics; the rates are percentages.
timezone, location_country, location_city Detected from the subscriber’s activity, if known.

GET /subscribers

Parameter Meaning
filter[status] active (default), unsubscribed, unconfirmed, bounced, junk or all.
filter[group] A group ID: only its members.
limit, cursor Paging, newest subscriber first (Pagination).
Terminal window
curl -g -H "Authorization: Bearer $ECHO_TOKEN" \
"$ECHO_API/subscribers?filter[group]=257e83a2-d55e-4a8c-b9a4-b3e939c151cc&limit=1"
{"data":[{"id":"98cc16c4-cb25-4ec6-8dbe-9704c4e198c4","email":"[email protected]","status":"active","source":"import","fields":{"name":"Réka","rendelesek_szama":5},"groups":[{"id":"257e83a2-d55e-4a8c-b9a4-b3e939c151cc","name":"Webshop vásárlók"}],"signup_ip":null,"optin_ip":null,"subscribed_at":"2026-10-22T08:52:08.762873Z","opted_in_at":null,"unsubscribed_at":null,"timezone":null,"location_country":null,"location_city":null,"sent":0,"opens":0,"clicks":0,"open_rate":0,"click_rate":0,"created_at":"2026-10-22T08:52:08.762873Z","updated_at":"2026-10-22T08:52:08.762873Z"}],"meta":{"next_cursor":"ATFli9bJvH7vgqoL19WIaHd7P2ajK_3_gc-avJBnE93Vz7-BggnUFyd3INX0aogupEIO9GInVy69os7dJEXCtecC72rG","prev_cursor":null,"limit":1,"total":2}}

The same list for one group or segment is also at GET /groups/{id}/subscribers and GET /segments/{id}/subscribers (with filter[status], limit, cursor).

The next page comes with cursor set to the next_cursor. Its prev_cursor leads back, also through cursor (a subscriber list’s prev_cursor starts with b.; pass it as it is):

Terminal window
curl -g -H "Authorization: Bearer $ECHO_TOKEN" \
"$ECHO_API/subscribers?filter[group]=257e83a2-d55e-4a8c-b9a4-b3e939c151cc&limit=1&cursor=ATFli9bJvH7v..."
{"data":[...],"meta":{"next_cursor":null,"prev_cursor":"b.ATFli9bJvH7vuyCx2u4V-jDq5wBEHY5rFldF82H_JvI92h0J4F4OC4I6KKepvM_8mmxMtw6iIl72qgodfvxCaS2-bJj7","limit":1,"total":2}}

A very large, slow filter can answer 503 list_timeout: narrow the filter and try again.

GET /subscribers/count takes the same filter[status] and filter[group].

Terminal window
curl -g -H "Authorization: Bearer $ECHO_TOKEN" "$ECHO_API/subscribers/count?filter[status]=unsubscribed"
{"data":{"total":193}}

total is null when counting took too long.

POST /subscribers adds a subscriber, or updates the one that already has this email address.

Key Required Meaning
email yes The address. It is trimmed and lower-cased.
fields no Values by field key. null clears a value; an empty string leaves the stored value as it is.
groups no Group IDs to add the subscriber to. Groups are only added, never removed.
ip_address no The sign-up IP address, stored for a new subscriber.
Terminal window
curl -X POST "$ECHO_API/subscribers" \
-H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
-d '{"email":"[email protected]","fields":{"name":"Lilla","last_name":"Farkas","city":"Szeged","rendelesek_szama":3},"groups":["257e83a2-d55e-4a8c-b9a4-b3e939c151cc"],"ip_address":"203.0.113.25"}'

201 Created for a new subscriber:

{"data":{"id":"d5f0b4b1-b59d-40eb-b2e0-74857893a055","email":"[email protected]","status":"unconfirmed","source":"api","fields":{"city":"Szeged","last_name":"Farkas","name":"Lilla","rendelesek_szama":3},"groups":[{"id":"257e83a2-d55e-4a8c-b9a4-b3e939c151cc","name":"Webshop vásárlók"}],"signup_ip":"203.0.113.25","optin_ip":null,"subscribed_at":"2026-10-22T08:52:08.527488Z","opted_in_at":null,"unsubscribed_at":null,"timezone":null,"location_country":null,"location_city":null,"sent":0,"opens":0,"clicks":0,"open_rate":0,"click_rate":0,"created_at":"2026-10-22T08:52:08.527488Z","updated_at":"2026-10-22T08:52:08.527488Z"}}

Sending the same address again updates it and answers 200 OK. Only the given fields change:

Terminal window
curl -X POST "$ECHO_API/subscribers" \
-H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
-d '{"email":"[email protected]","fields":{"vasarloi_szint":"Ezüst"}}'
{"data":{"id":"d5f0b4b1-b59d-40eb-b2e0-74857893a055","email":"[email protected]","status":"unconfirmed","source":"api","fields":{"city":"Szeged","last_name":"Farkas","name":"Lilla","rendelesek_szama":3,"vasarloi_szint":"Ezüst"},"groups":[{"id":"257e83a2-d55e-4a8c-b9a4-b3e939c151cc","name":"Webshop vásárlók"}],"signup_ip":"203.0.113.25","optin_ip":null,"subscribed_at":"2026-10-22T08:52:08.527488Z","opted_in_at":null,"unsubscribed_at":null,"timezone":null,"location_country":null,"location_city":null,"sent":0,"opens":0,"clicks":0,"open_rate":0,"click_rate":0,"created_at":"2026-10-22T08:52:08.527488Z","updated_at":"2026-10-22T08:52:08.568881Z"}}

Status of a new subscriber. If double opt-in is on for API sign-ups in the workspace’s subscribe settings, a new subscriber starts unconfirmed and gets the confirmation email; after confirming they become active. If it’s off, they start active. An existing subscriber keeps their status: the API never resubscribes someone who unsubscribed.

Refused addresses answer 422 validation with the reason in fields.email:

Case fields.email (English message)
Not a valid address Enter a valid email address.
A role address such as abuse@ or postmaster@ Role addresses such as abuse@ or postmaster@ can't be added.
On the suppression list (bounced or spam complaint before) This address is on the suppression list because it bounced or reported spam.
{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"email":"This address is on the suppression list because it bounced or reported spam."}}}

Other field errors name the key with a fields. prefix:

{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"fields.rendelesek_szama":"Enter a number, e.g. 12 or 3.5."}}}
{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"fields.kedvenc_szin":"Unknown field."}}}

An unknown group ID: {"fields":{"groups":"Choose an existing group."}}. An unknown top-level key (for example tags): {"fields":{"tags":"Unknown field."}}.

GET /subscribers/{id} takes the subscriber’s ID or email address (URL-encode the @ as %40 if your client needs it):

Terminal window
curl -H "Authorization: Bearer $ECHO_TOKEN" "$ECHO_API/subscribers/[email protected]"

The answer is {"data": <subscriber>}, or 404 not_found:

{"error":{"code":"not_found","message":"Not found."}}

Only GET takes an email address; the other subscriber endpoints need the ID.

PUT /subscribers/{id} changes field values and adds groups in one transaction. Every key is optional.

Key Meaning
fields Values by field key; null clears a value.
groups Group IDs to add (never removed).
status Only unsubscribed (below).
Terminal window
curl -X PUT "$ECHO_API/subscribers/d5f0b4b1-b59d-40eb-b2e0-74857893a055" \
-H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
-d '{"fields":{"city":"Pécs","company":null}}'
{"data":{"id":"d5f0b4b1-b59d-40eb-b2e0-74857893a055","email":"[email protected]","status":"unconfirmed","source":"api","fields":{"city":"Pécs","last_name":"Farkas","name":"Lilla","rendelesek_szama":3,"vasarloi_szint":"Ezüst"},"groups":[{"id":"257e83a2-d55e-4a8c-b9a4-b3e939c151cc","name":"Webshop vásárlók"}],"signup_ip":"203.0.113.25","optin_ip":null,"subscribed_at":"2026-10-22T08:52:08.527488Z","opted_in_at":null,"unsubscribed_at":null,"timezone":null,"location_country":null,"location_city":null,"sent":0,"opens":0,"clicks":0,"open_rate":0,"click_rate":0,"created_at":"2026-10-22T08:52:08.527488Z","updated_at":"2026-10-22T08:52:10.212948Z"}}

The email address can’t be changed through the API.

Terminal window
curl -X PUT "$ECHO_API/subscribers/d5f0b4b1-b59d-40eb-b2e0-74857893a055" \
-H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
-d '{"status":"unsubscribed"}'

The answer is the subscriber with "status":"unsubscribed" and unsubscribed_at set. Any other status is refused, because only the subscriber can give consent again:

{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"status":"Through the API a subscriber can only be unsubscribed."}}}
Terminal window
curl -X POST "$ECHO_API/subscribers/d5f0b4b1-b59d-40eb-b2e0-74857893a055/groups/257e83a2-d55e-4a8c-b9a4-b3e939c151cc" \
-H "Authorization: Bearer $ECHO_TOKEN"

200 OK with the subscriber. Adding an existing member changes nothing.

Terminal window
curl -X DELETE "$ECHO_API/subscribers/d5f0b4b1-b59d-40eb-b2e0-74857893a055/groups/257e83a2-d55e-4a8c-b9a4-b3e939c151cc" \
-H "Authorization: Bearer $ECHO_TOKEN"

204 No Content. Adding to a group can start an automation workflow whose trigger is joining that group.

Terminal window
curl -X DELETE "$ECHO_API/subscribers/8273ff7d-cb69-4a72-83e6-dbebbec33115" -H "Authorization: Bearer $ECHO_TOKEN"

204 No Content. The subscriber is removed; their past events stay in the campaign statistics without the person. Echo no longer knows that a deleted address unsubscribed, so adding the address again later makes it a new subscriber. To stop emails but keep the record, unsubscribe instead.

Terminal window
curl -X POST "$ECHO_API/subscribers/98cc16c4-cb25-4ec6-8dbe-9704c4e198c4/forget" -H "Authorization: Bearer $ECHO_TOKEN"

204 No Content, and the erasure is complete when the answer arrives: the subscriber, their notes and memberships and the personal details of their events are deleted. The address may be added again later as a new subscriber. A subscriber.deleted webhook for a forgotten subscriber carries only the ID (Webhooks).

GET /subscribers/{id}/activity lists the subscriber’s events, newest first, 50 a page. It pages forward only: pass next_cursor in cursor; prev_cursor is always null.

filter Events
all (default) Everything
campaign_emails, automation_emails Campaign or workflow emails sent
opens, clicks Opens, clicks
bounces, soft_bounces, junks Permanent bounces, soft bounces, spam complaints
unsubscribes, forwards Unsubscribes, forwards
marketing_preferences, preference_center Preference changes
Terminal window
curl -H "Authorization: Bearer $ECHO_TOKEN" \
"$ECHO_API/subscribers/1022eacd-0544-4c99-9b38-c678933ed6b9/activity?filter=clicks"
{"data":[{"event_id":"87949","type":"click","source_kind":"campaign","occurred_at":"2026-10-01T08:25:03.153043Z","data":{"link":1,"ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"},"campaign":{"id":"522e9156-b62c-45df-8fa8-4d93715379fa","name":"VIP: őszi zártkörű vásár"},"automation":null},{"event_id":"87948","type":"click","source_kind":"campaign","occurred_at":"2026-10-01T08:24:03.153043Z","data":{"link":1,"ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"},"campaign":{"id":"522e9156-b62c-45df-8fa8-4d93715379fa","name":"VIP: őszi zártkörű vásár"},"automation":null},{"event_id":"51770","type":"click","source_kind":"campaign","occurred_at":"2026-08-06T07:38:17.436946Z","data":{"link":1,"ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"},"campaign":{"id":"d5ca5c93-93d3-49ad-8d0f-3a069952faee","name":"Őszi felmérés: mit ültetnél?"},"automation":null}],"meta":{"next_cursor":null,"prev_cursor":null,"limit":50}}

type is one of sent, delivered, open, click, bounce_hard, bounce_soft, complaint, unsubscribe, forward, preference_change, preference_center, optin_sent, preference_email. source_kind is campaign, automation or null. data depends on the type (for a click: the link’s number in the email and the user agent) and may gain keys later, so read it leniently. campaign and automation name the campaign or the workflow whose email the event belongs to (id and name), or are null when there is none or it has been deleted since.

A malformed or expired cursor answers 422 with fields.cursor:

{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"cursor":"Invalid value."}}}