Skip to main content

REST API v1

Everything the app can do, as an endpoint.

Manage contacts, build and send campaigns, read reporting, subscribe to webhooks, and call the AI copy tools, from your own systems.

Base URL

https://tweak.email/api/v1

Authentication

Authorization: Bearer <your-api-key>

Or the X-Api-Key header. API access is on the Business plan; generate a key in Settings.

Your first call

Confirm the credential works and see what it can reach:

curl https://tweak.email/api/v1/me \
  -H "Authorization: Bearer $TWEAK_API_KEY"

Add a contact. The write is an upsert on email address, so re-running it is safe:

curl -X POST https://tweak.email/api/v1/contacts \
  -H "Authorization: Bearer $TWEAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","first_name":"Jane","tag_ids":[3],"consent_attested":true}'

A contact who has already unsubscribed keeps that status no matter what you send, so a full-address-book re-sync can never quietly resubscribe someone.

consent_attested is required on contact creates and bulk upserts: it confirms the person gave permission to receive email from you and was not taken from a purchased, rented or scraped list. Requests without it are refused with 422.

Idempotency

Send an Idempotency-Key header on any write. A retry with the same key replays the original response instead of executing again.

-H "Idempotency-Key: order-4417-receipt"

Use it on campaign sends especially: a duplicated send is the one mistake here you can't undo. Keys are honoured for 24 hours.

Scoped keys

Issue one key per integration with only the scopes it needs, then revoke that one without disturbing the others.

contacts:read contacts:write campaigns:read campaigns:write campaigns:send events:read events:write suppressions:read suppressions:write transactional:send ai:write webhooks:read webhooks:write

A :write grant implies the matching :read. Sending is its own scope, so a key that drafts campaigns can't put mail in front of customers.

Webhook signatures

Every delivery carries a Tweak-Signature header. Verify it against the raw request body before trusting the payload.

Tweak-Signature: t=1754835600,v1=9f86d0818...

hmac_sha256(secret, "{t}.{raw_body}") == v1

The timestamp is inside the signed material, so a captured payload can't be replayed at you indefinitely. Reject anything older than a few minutes.

Unified event pipeline

Every delivery event, whether from provider webhooks, native sends, or your own API calls, flows through one pipeline, and back out again over GET /events and your webhooks.

  • Campaign stats updated automatically
  • Auto-suppress on bounce, spam, unsubscribe
  • Contact statuses synced in real time
  • Switch providers, keep your history

Reverse ETL

Connect BigQuery, pick an audience without a CSV, and use approved warehouse fields as merge tags at send time.

  • OAuth with read-only BigQuery scope
  • Browse datasets, tables, and columns
  • Map columns to email placeholders
  • Approved fields resolved during sending

Rate limits

Counted per API key, so one integration's burst never eats another's headroom. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining.

Requests (Business)600/min
Events per request1,000
Contacts per bulk call1,000
Records per page100

Error responses

Standard HTTP status codes with JSON error bodies.

401Missing, invalid, revoked, or expired key
403Missing scope, or plan excludes API access
404No such record on your team
422Validation error, or a send gate failed
429Rate limited, see Retry-After

A refused send returns a blockers array naming each unmet requirement. POST /campaigns/{id}/preflight returns the same list without attempting to send.

Business plan

Build on it today

Generate a scoped API key in Settings, point your client at the OpenAPI spec, and ship.

Start free