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.

ParameterDefaultNotes
statusallOne of pending, active, unsubscribed, bounced, complained
limit50Clamped to 1–200
offset0Offset 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

StatusMeaning
pendingSigned up, hasn't clicked the confirmation link. Receives no campaigns.
activeConfirmed. Receives campaigns.
unsubscribedOpted out. Suppressed permanently.
bouncedDelivery hard-failed. Suppressed.
complainedMarked 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.