REST API
A small API for real email.
All writes are workspace-scoped. API keys can be restricted to one domain and to explicit scopes.
Authentication
Send the API key as a bearer token. The raw key is displayed once and stored only as a SHA-256 hash.
Authorization: Bearer ts_your_keySend an email
Supply inline content or a template ID. Use an idempotency key for every business event.
curl https://your-tinysend.example/api/v1/emails \
-H "Authorization: Bearer ts_your_key" \
-H "Idempotency-Key: receipt-order-123" \
-H "Content-Type: application/json" \
-d '{"to":"[email protected]","subject":"Your receipt","html":"<h1>Thank you</h1>"}'The API returns 202 Accepted. Poll GET /api/v1/emails/:id for status.
Batch sends
POST /api/v1/emails/batch accepts up to 100 items. Each result reports its own accepted or failed status.
Synchronize users
POST /api/v1/contacts/sync upserts one contact or up to 1,000 contacts by normalized email.
curl https://your-tinysend.example/api/v1/contacts/sync \
-H "Authorization: Bearer ts_your_key" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","externalId":"user_42","attributes":{"plan":"pro"}}'Templates
List current versions with GET /api/v1/templates. Dashboard edits create immutable versions, and transactional sends always use the current version.
Broadcasts
Create a draft with POST /api/broadcasts, then send it with POST /api/broadcasts/:id/send. Broadcasts include every subscribed contact, exclude suppressions, and append a signed unsubscribe link.
Errors
Responses use standard HTTP statuses. Validation errors include field-level issues. Every response includes X-Request-Id for logs.
npm SDK
import { Tinysend } from '@tinysend/sdk'
const email = new Tinysend({ apiKey: process.env.TINYSEND_API_KEY })
await email.emails.send({
to: '[email protected]',
templateId: 'template-uuid',
variables: { firstName: 'Ada' }
}, { idempotencyKey: 'welcome-user-42' })