SigFollow
Sign in

API reference

Blacklist

A tenant-scoped phone blacklist: full audit history (one row per write), Redis-backed hot reads, and a one-shot 'unblacklist' for support cases.

Resource model. Each write produces a new PhoneBlacklist row — there is no unique constraint on (tenantId, phone). That keeps a full audit trail of who added the number, when, and why. Membership checks are deduplicated at read time via Redis.

Where blacklist hits land
Send-time enforcement is opt-in: pass ?filter_blacklist=true on POST /v1/:phoneNumberId/messages and SigFollow rejects calls to blacklisted recipients with HTTP 422. Without the flag, the blacklist is informational only (use /v1/blacklist/check in your own pipeline).

Check a phone

GET/v1/blacklist/check

Scope: blacklist:read

Hot-path membership query. First lookup hits the database and caches the positive result in Redis for 60s; subsequent calls in that window skip the database. Phone normalization is server-side.

Query
NameTypeRequiredDescription
phonestringyesPhone to check, 5–32 chars after normalization.

Example

curl -G https://api.sigfollow.com/v1/blacklist/check \
  -H "Authorization: Bearer sflo_live_xxx" \
  --data-urlencode "phone={{Recipient-Phone-Number}}"

Response

{
  "phone": "{{Recipient-Phone-Number}}",
  "blacklisted": true
}

phone echoes your original input (not the normalized form). blacklisted is true if at least one active blacklist row matches the normalized phone.

Add to blacklist

POST/v1/blacklist

Scope: blacklist:write

Creates a new row. Same phone written twice produces two rows— that's by design for audit. To "overwrite" semantics, call remove by phone first.

Body

NameTypeRequiredDescription
phonestringyesPhone to block. Normalization tolerant.
reasonstringnoAudit note, max 500 chars. Recommended for downstream traceability.

Example

curl -X POST https://api.sigfollow.com/v1/blacklist \
  -H "Authorization: Bearer sflo_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "{{Recipient-Phone-Number}}",
    "reason": "Reported as spam by customer support"
  }'

Response

{
  "id": "cm5bl123abcdef",
  "tenantId": "cm5xy000tenant",
  "phone": "{{Recipient-Phone-Number}}",
  "source": "API",
  "reason": "Reported as spam by customer support",
  "sessionId": null,
  "aiAgentId": null,
  "createdById": null,
  "createdAt": "2026-05-13T22:40:00.000Z"
}

Update reason

PATCH/v1/blacklist/:id

Scope: blacklist:write

Only reason is mutable — phone, source, and provenance fields are immutable audit columns. Pass null or omitreason to clear / leave unchanged.

Example

curl -X PATCH https://api.sigfollow.com/v1/blacklist/cm5bl123 \
  -H "Authorization: Bearer sflo_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Updated after re-verification" }'
No-op short-circuit
Sending the same reason as currently stored is detected server-side and skipped — no database write, no audit noise. Use this to safely retry PATCH calls.

Delete one row

DELETE/v1/blacklist/:id

Scope: blacklist:write

Deletes a single historical row. Other rows for the same phone remain — the phone stays blacklisted while at least one row exists. Use this when you need to remove a specific bad entry but keep the rest of the audit trail.

Example

curl -X DELETE https://api.sigfollow.com/v1/blacklist/cm5bl123 \
  -H "Authorization: Bearer sflo_live_xxx"

Un-blacklist a phone

DELETE/v1/blacklist/by-phone

Scope: blacklist:write

Wipes allrows for a given phone. The typical "remove from blacklist" customer support flow. Idempotent — returns removed: 0when the phone wasn't on the list.

Query
NameTypeRequiredDescription
phonestringyesPhone to clear, normalization tolerant.

Example

curl -X DELETE -G https://api.sigfollow.com/v1/blacklist/by-phone \
  -H "Authorization: Bearer sflo_live_xxx" \
  --data-urlencode "phone={{Recipient-Phone-Number}}"

Response

{ "removed": 2 }