Subscribers
Unlike the content endpoints, these have no public fallback. Subscriber emails are data people gave you, not published content, so every request needs a scoped secret key.
List subscribers
GET /api/v1/subscribers
Requires subscribers:read. The project comes from the key — there is no projectId
parameter here.
| Parameter | Default | Notes |
|---|---|---|
status | all | One of pending, active, unsubscribed, bounced, complained |
limit | 50 | Clamped to 1–200 |
offset | 0 | Offset pagination |
curl "https://api.inkform.dev/api/v1/subscribers?status=active&limit=100" \
-H "Authorization: Bearer inkform_sk_your_key"{
"data": [
{
"id": "b7c1...",
"email": "reader@example.com",
"status": "active",
"source": "footer",
"subscribed_at": "2026-09-01T10:04:11.000Z"
}
]
}An invalid status value is ignored rather than rejected — you get the unfiltered list.
Check the value you send if a filter seems to do nothing.
This response has no pageInfo or totalCount. Page until you get fewer rows than your
limit.
Statuses
| Status | Meaning |
|---|---|
pending | Signed up, hasn't clicked the confirmation link. Receives no campaigns. |
active | Confirmed. Receives campaigns. |
unsubscribed | Opted out. Suppressed permanently. |
bounced | Delivery hard-failed. Suppressed. |
complained | Marked your mail as spam. Suppressed. |
Only active subscribers are sent campaigns. See
Subscribers & analytics for the lifecycle in full.
Add a subscriber
POST /api/v1/subscribers
Requires subscribers:write.
{ "email": "reader@example.com", "source": "crm-import" }source is a free-form label (max 80 characters) that shows up in the dashboard — use it
to tell your signup surfaces apart.
curl -X POST "https://api.inkform.dev/api/v1/subscribers" \
-H "Authorization: Bearer inkform_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"email":"reader@example.com","source":"crm-import"}'{ "data": { "id": "b7c1...", "email": "reader@example.com", "status": "pending" } }Double opt-in is not bypassable
This endpoint creates a pending subscriber and sends the same confirmation email the public signup form sends. There is no flag to skip it, and the confirmation token is deliberately not returned in the response — returning it would let whoever holds the API response confirm on the subscriber's behalf, which is exactly the consent guarantee double opt-in exists to provide.
So there is no bulk "pre-verified import" path. Importing a list means every address gets
a confirmation email and only the ones who click become active. That's the intended
behaviour, not a limitation to work around: mailing addresses that never confirmed is how
you end up on a blocklist.
Adding an address that's already pending will not re-send the confirmation email if
one went out in the last 15 minutes. See Newsletter limits.
Public signup instead
If you just want readers to subscribe from your site, don't put a secret key in your frontend to call this endpoint. Use the public signup endpoint, which is anonymous by design — see Add newsletter signup to a site.