Skip to content
Docs menu

Authentication

Every ContactFollowUp API call needs a bearer token. Tokens belong to a service account — never to a user directly.

Service accounts

A service account is a non-human identity in your workspace. It has a role (ADMIN, MANAGER, AGENT, or READONLY) and a set of issued tokens. The role decides what the token can do — it maps to the same capabilities the user roles use, so an AGENT-scoped token can write contacts but not manage users.

Create one in Settings → Service accounts. You get the plaintext token once, at issuance; we only store its hash. Lose it and you have to rotate.

Token format

Tokens look like hns_<saId>.<secret>. The prefix hns_ makes them grep-able in logs and the embedded service-account id lets the server look up the account before doing the constant-time secret comparison.

example token (truncated)
hns_clm5kqe5d000308l2hga09c2x.NDk2NWYzZGY4MmYyNzgyNzAzMzZkYTM4

Sending a request

Set the header on every request:

HTTP
GET /api/contacts HTTP/1.1
Host: app.contactfollowup.com
Authorization: Bearer hns_clm5kqe5d000308l2hga09c2x.NDk2NWYzZGY4MmYyNzgyNzAzMzZkYTM4
Accept: application/json
cURL
export HEARTHNOTE_TOKEN=hns_xxxxxxx.yyyyyyyyyyyyyyyy

curl https://app.contactfollowup.com/api/contacts \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN"

If the header is missing or wrong

Missing or malformed tokens return 401 Unauthorized. A token that was once valid but has since been revoked or expired also returns 401 — never 403. A valid token whose role lacks the capability returns 403 Forbidden.

Scopes (capabilities)

ContactFollowUp uses role-based capabilities rather than per-token scope flags. The role attached to the service account decides what it can do. Endpoint pages call out the capability they require; the full matrix:

CapabilityADMINMANAGERAGENTREADONLY
contacts.read
contacts.write
contacts.delete
deals.read
deals.write
sequences.manage
campaigns.manage
audit.read
users.manage

Rotation

Tokens have no required expiry — leave them alone forever if you want — but you can set a TTL per token at issuance. To rotate without downtime: issue the new token, deploy your services with both old and new available, switch traffic, then revoke the old one.

Revoke from the UI
# Settings → Service accounts → pick account → tokens → "Revoke"
# Revoked tokens 401 on every subsequent call; the revoke event is audited.

Can I use a user session instead?

Sort of. The same routes accept the cookie session you'd get from logging in to the web app, because the UI uses these endpoints too. But it's not a stable contract — login cookies are rotated, CSRF-protected, and tied to a browser. For programmatic access always use a service account.