Home
Developer Hub
Dashboard

Developer Hub

Connect apps, AI agents, and automation systems to Sendinel in minutes. Start with an API key, wire up REST or MCP, then use llms.txt when an agent needs the compact machine-readable reference.

For AI Agents & LLMs

Point agents at the compact llms.txt reference or the expanded MCP + REST reference before they call Sendinel.

Quick Start

Sendinel is an email operations control plane. Send transactional email, run campaigns, track events, and manage contacts — all through a REST API or the MCP server.

No DNS required to start. Every Sendinel account ships with access to mail.sendinel.ai — a shared sending domain that's already configured, warmed, and ready to send. You can add your own domain any time, but you don't need one to get your first email out the door.

Get your API key

Go to Dashboard → Settings → Developer and create a new key. Keys are prefixed with snk_ and scoped to read, write, or admin. Most integrations need write scope.

Keys are hashed (SHA-256) before storage. The plaintext is shown once and cannot be retrieved again.

Option A — TypeScript SDK (recommended)

bash
npm install @sendinel/sdk
TypeScript
import { Sendinel } from "@sendinel/sdk";

const sendinel = new Sendinel({ apiKey: "snk_..." });

// Identify a user — creates or updates their contact profile
await sendinel.identify("user@example.com", {
  firstName: "Alex",
  properties: { plan: "pro" },
  siteId: "your-site-uuid",   // subscribes them + triggers signup campaigns
});

// Fire a named event — triggers any matching campaign automations
await sendinel.track("user@example.com", "completed_onboarding");

Option B — curl

curl
curl -X POST https://sendinel.ai/api/v1/identify \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "first_name": "Alex",
    "properties": { "plan": "pro" },
    "site_id": "your-site-uuid"
  }'

Copy-paste flows

fetch — enroll a contact in a triggered sequence
const SENDINEL_API_KEY = process.env.SENDINEL_API_KEY;
const baseUrl = "https://sendinel.ai";

await fetch(`${baseUrl}/api/v1/identify`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${SENDINEL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "alex@acme.com",
    first_name: "Alex",
    properties: { plan: "pro" },
    site_id: "your-site-uuid",
  }),
});

const eventRes = await fetch(`${baseUrl}/api/v1/events`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${SENDINEL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "alex@acme.com",
    event: "completed_onboarding",
    data: { source: "app" },
  }),
});

console.log(await eventRes.json());
fetch — transactional email with tracking and idempotency
const res = await fetch("https://sendinel.ai/api/v1/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SENDINEL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "alex@acme.com",
    site_id: "your-site-uuid",
    subject: "Your order is confirmed",
    html: "<h1>Order #123</h1><p>Ships tomorrow.</p>",
    text: "Order #123 ships tomorrow.",
    tags: ["transactional", "order"],
    idempotency_key: "order-123",
    tracking: true,
  }),
});

if (!res.ok) throw new Error(await res.text());
console.log(await res.json());

Machine-Readable References

Sendinel exposes canonical references for humans, SDK generators, and AI agents. Use these instead of scraping this page when you need a complete machine-readable contract.

ReferenceBest forURL
REST OpenAPISDK generation, endpoint discovery, typed REST clients/api/v1/openapi.json
MCP OpenAPINon-MCP agents that call tools over HTTP/api/mcp/openapi.json
Compact LLM referenceSmall prompt/context budgets/llms.txt
Full LLM referenceAgents that need payload shapes, safety rules, and examples/llms-full.txt

For AI Agents & LLMs

Sendinel publishes llms.txt files so coding agents and LLM tools can discover the API, MCP server, auth model, and safety notes without scraping the whole site. The convention comes from llmstxt.org: expose a concise, stable text reference at the root of the domain for agent consumption.

Compact API reference for AI tools
Short reference for auth, REST endpoints, MCP URL, and common integration flows.
/llms.txt
Full MCP + REST reference for LLM consumption
Expanded reference with all tool groups, payload shapes, safety constraints, and examples.
/llms-full.txt

Agent connection path — remote (recommended)

StepAction
1In Settings → AI, click "Connect to Claude.ai" or copy the MCP URL.
2For Claude Code: run the command below. Your browser opens the OAuth approval page.
3Approve access — tools appear automatically. No API key or config file needed.
Claude Code — one-command connect
claude mcp add sendinel --url https://sendinel.ai/mcp

Agent connection path — local stdio (advanced)

StepAction
1Create a project API key in Dashboard → Settings → Developer.
2Add @sendinel/mcp-server as a local stdio MCP server and inject SENDINEL_API_KEY.
3Use /llms.txt for compact instructions or /llms-full.txt for the complete tool reference.
claude_desktop_config.json — stdio MCP config
{
  "mcpServers": {
    "sendinel": {
      "command": "npx",
      "args": ["-y", "@sendinel/mcp-server@latest"],
      "env": {
        "SENDINEL_API_KEY": "snk_your_api_key"
      }
    }
  }
}

REST API Reference

REST endpoints use Bearer token authentication with project-scoped snk_ keys. Send JSON requests to https://sendinel.ai; self-hosted installs can override the base URL.

EndpointPurposeDetails
POST /api/v1/identifyCreate or update contactsIdempotent by email; can subscribe contacts to a site.
POST /api/v1/eventsTrack named eventsTriggers event-based campaign automations.
POST /api/v1/sendSend transactional emailSupports idempotency keys to prevent duplicate sends.
POST /api/v1/conversionTrack revenue or conversion eventsAttributes conversion data back to campaigns and contacts.
GET /api/v1/contactsList contactsSupports limit, cursor, site_id, tag, and search filters.
POST /api/v1/contactsCreate a contactSupports Idempotency-Key and accepts tags, attribution fields, and site_id.
GET /api/v1/sitesList sending sitesReturns configured sender identities for the authenticated project.
GET /api/v1/campaignsList campaignsFilter by site_id and status; paginated with limit and cursor.
POST /api/v1/campaignsCreate campaign shellCreates a draft campaign for a site; add steps with campaign step endpoints.
GET /api/v1/templatesList templatesFilter by category/search; paginated with limit and cursor.
POST /api/v1/templatesCreate templateCreates a reusable AI/template brief with optional subject hint and HTML.
GET /api/v1/analytics/overviewSite analytics summaryRequires site_id; returns delivery, growth, and sender health metrics.
GET /api/v1/analytics/campaignsCampaign analyticsReturns campaign-level performance metrics.
GET /api/v1/analytics/conversionConversion analyticsReturns attributed conversion and revenue metrics.
GET /api/v1/exportTenant exportAdmin-scoped JSON/CSV export for a site.

Common REST examples

curl — list contacts
curl "https://sendinel.ai/api/v1/contacts?site_id=site_uuid&limit=50&tag=paying" \
  -H "Authorization: Bearer snk_your_api_key"
curl — create contact with idempotency
curl -X POST https://sendinel.ai/api/v1/contacts \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: contact-alex-acme" \
  -d '{
    "email": "alex@acme.com",
    "first_name": "Alex",
    "last_name": "Smith",
    "site_id": "site_uuid",
    "tags": ["paying", "enterprise"],
    "utm_source": "stripe"
  }'
curl — create template
curl -X POST https://sendinel.ai/api/v1/templates \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: template-trial-expiry-v1" \
  -d '{
    "name": "Trial expiry reminder",
    "brief": "Remind the user their trial ends soon and ask them to choose a plan.",
    "subject_hint": "Your trial ends in 3 days",
    "category": "lifecycle",
    "tags": ["trial", "conversion"]
  }'
curl — create campaign shell
curl -X POST https://sendinel.ai/api/v1/campaigns \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: campaign-welcome-v1" \
  -d '{
    "name": "Welcome sequence",
    "site_id": "site_uuid",
    "type": "drip"
  }'
curl — site overview analytics
curl "https://sendinel.ai/api/v1/analytics/overview?site_id=site_uuid&days=30" \
  -H "Authorization: Bearer snk_your_api_key"
curl — tenant export
curl "https://sendinel.ai/api/v1/export?site_id=site_uuid&format=json" \
  -H "Authorization: Bearer snk_admin_api_key"

SDKs & Integrations

@sendinel/sdk is a zero-dependency TypeScript client for the Sendinel v1 API. Works in Node.js 18+ (uses native fetch). Full type coverage included.

bash
npm install @sendinel/sdk

Full example

TypeScript
import { Sendinel, SendinelError } from "@sendinel/sdk";

const sendinel = new Sendinel({
  apiKey: process.env.SENDINEL_API_KEY!,
  // baseUrl: "https://sendinel.ai"  // default, override for self-hosted
});

// Identify — creates or updates a contact profile (idempotent by email)
const { contact_id, created } = await sendinel.identify("alex@example.com", {
  firstName: "Alex",
  lastName: "Smith",
  properties: { plan: "pro", company: "Acme" },
  tags: ["paying", "enterprise"],
  siteId: "your-site-uuid",  // subscribes to site + enrolls in signup campaigns
});

// Track — fires a named event, auto-creates contact if needed
const result = await sendinel.track("alex@example.com", "completed_onboarding", {
  steps_completed: 5,
  duration_seconds: 142,
});
// result.enrolled_campaigns  →  ["Welcome to Pro"]
// result.contact_created     →  false (contact already existed)

// Send — transactional email, one-off
await sendinel.send({
  to: "alex@example.com",
  siteId: "your-site-uuid",
  subject: "Your order is confirmed",
  html: "<p>Order #123 ships tomorrow.</p>",
  idempotencyKey: "order-123",  // prevents duplicate sends
});

Error handling

TypeScript
import { Sendinel, SendinelError } from "@sendinel/sdk";

try {
  await sendinel.identify("bad-email");
} catch (err) {
  if (err instanceof SendinelError) {
    console.log(err.status);  // 400
    console.log(err.message); // "Invalid email address"
    console.log(err.body);    // full JSON response
  }
}

Retry behavior

The SDK retries automatically on network errors, 429 Too Many Requests, and 5xx responses. Up to 3 attempts with exponential backoff (100ms, 200ms, 400ms). Respects Retry-After headers. 4xx errors (except 429) are not retried — they indicate a problem with the request.

Connections

Everything Sendinel can connect to — email delivery providers, social platforms, import sources, webhooks, and AI clients.

Email Delivery Providers

Sendinel routes email through your provider of choice. Configure at Settings → Project → Email Provider. Credentials are stored AES-256-GCM encrypted.

ResendDefault
API key
SendGrid
API key
Postmark
Server token
Amazon SES
Region + access key
Mailgun
API key + domain

Social Publishing Platforms

Sendinel publishes to 12 social platforms through the SocialPublisher abstraction (Upload Post by default). Connect accounts at Settings → Channels → Social → Connect Account. OAuth flow: GET /api/integrations/social/{{platform}}/connect.

Instagram
Video · Photo
LinkedIn
Video · Photo · Text · Doc
Twitter / X
Video · Photo · Text
Facebook
Video · Photo · Text
TikTok
Video
YouTube
Video
Pinterest
Video · Photo
Reddit
Video · Photo · Text
Threads
Photo · Text
Bluesky
Photo · Text
Google Business
Photo · Text

Import Sources

Migrate contacts, lists, tags, templates, campaigns, and suppressions into Sendinel from your existing platform. Trigger at Settings → Import Contacts.

Customer.io
API key + site ID
Mailchimp
API key
Klaviyo
Private API key
ActiveCampaign
Account URL + API key
Kit
API key
Resend
API key

Outbound Webhooks

Sendinel fires signed HTTP POST webhooks to your endpoint on key events. Configure at Settings → Developer → Webhooks. Signature: X-Sendinel-Signature (HMAC-SHA256).

campaign.completed
email.bounced
email.complained
contact.unsubscribed
bounce_rate.warning
domain.dns_lost

AI Clients & MCP

Sendinel's 65-tool MCP server can be connected from any MCP-compatible AI client. Known integrations: Synchronex (primary orchestration), Claude Desktop, Cursor.

claude_desktop_config.json
{
  "mcpServers": {
    "sendinel": {
      "command": "npx",
      "args": ["-y", "@sendinel/mcp-server@latest"],
      "env": {
        "SENDINEL_API_KEY": "snk_...",
        "SENDINEL_PROJECT_ID": "uuid"
      }
    }
  }
}

Identify API

POST /api/v1/identify

Create or update a contact profile. Safe to call on every user action — idempotent by email address. Authenticated via API key with write scope.

This is the standard integration point. Call identify when a user signs up, logs in, or upgrades — same pattern as Segment and Customer.io. Sendinel merges properties, appends tags, and handles deduplication automatically.

Request body

application/json
{
  "email": "user@example.com",       // required
  "first_name": "Alex",              // optional
  "last_name": "Smith",              // optional
  "properties": {                    // optional — merged (patch semantics, never overwrites)
    "plan": "pro",
    "company": "Acme"
  },
  "tags": ["paying", "enterprise"],  // optional — appended, no duplicates
  "site_id": "uuid"                  // optional — subscribes to site + triggers signup campaigns
}

Response — 201 Created (new contact)

201 Created
{
  "contact_id": "uuid",
  "created": true
}

Response — 200 OK (existing contact updated)

200 OK
{
  "contact_id": "uuid",
  "created": false
}

Merge semantics

FieldBehavior on update
emailPrimary key — not updatable
first_name / last_nameOverwritten only if non-empty string provided
propertiesDeep merge — new keys added, existing keys updated only if re-specified
tagsAppended — existing tags preserved, duplicates removed
site_idUpserts site subscription, enrolls in signup campaigns (first create only)

Examples

curl
curl -X POST https://sendinel.ai/api/v1/identify \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@acme.com",
    "first_name": "Alex",
    "properties": { "plan": "pro", "source": "stripe" },
    "tags": ["paying"],
    "site_id": "your-site-uuid"
  }'
TypeScript (SDK)
const { contact_id, created } = await sendinel.identify("alex@acme.com", {
  firstName: "Alex",
  properties: { plan: "pro", source: "stripe" },
  tags: ["paying"],
  siteId: "your-site-uuid",
});

Events API

POST /api/v1/events

Fire a named event for a contact. Enrolls them in any active triggered campaigns that match the event name. Authenticated via API key with write scope.

Auto-identify on track. If the contact doesn't exist yet, Sendinel creates a minimal profile automatically (email only, source: "api") so the event can be recorded and campaigns can enroll. You don't need to call identify first — though calling both gives you richer contact data.

Request body

application/json
{
  "email": "user@example.com",          // required
  "event": "completed_onboarding",      // required — must match a campaign trigger_event_name
  "data": { "steps_completed": 5 }      // optional — stored in event metadata
}

Success response

200 OK
{
  "event": "completed_onboarding",
  "contact_id": "uuid",
  "contact_created": false,              // true if contact was auto-created
  "enrollments_created": 1,
  "enrolled_campaigns": ["Onboarding Sequence"],
  "skipped_campaigns": []               // already enrolled in these
}

Examples

curl
curl -X POST https://sendinel.ai/api/v1/events \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@acme.com",
    "event": "completed_onboarding",
    "data": { "steps_completed": 5 }
  }'
TypeScript (SDK)
const result = await sendinel.track("alex@acme.com", "completed_onboarding", {
  steps_completed: 5,
});

console.log(result.enrolled_campaigns); // ["Onboarding Sequence"]
console.log(result.contact_created);    // false

How campaign triggering works

When an event is received, Sendinel finds all active campaigns with type: "triggered"and a matching trigger_event_name. For each match, a new enrollment is created if the contact is not already enrolled (any non-exited status). The contact begins receiving the campaign sequence starting from the first step.

If no campaigns match the event name, a 200 is returned with enrollments_created: 0. You can fire test events from the dashboard: open a triggered campaign and click Send Test Event.

Transactional Email API

POST /api/v1/send

Send a single transactional email immediately. Checks plan limits, rate limits, and the suppression list before sending. The from address and name come from the site configuration. Authenticated via API key with write scope.

Request body

application/json
{
  "to": "user@example.com",             // required — recipient email
  "site_id": "uuid",                     // required — identifies sender domain + from address
  "subject": "Order confirmed",          // required — max 998 characters
  "html": "<p>Your order #123...</p>",   // required — HTML body
  "text": "Your order #123...",          // optional — plain text fallback
  "reply_to": "support@company.com",    // optional
  "headers": { "X-Custom": "value" },   // optional — custom headers
  "tags": ["transactional", "order"],   // optional — for filtering in email log
  "idempotency_key": "order-123",        // optional — prevents duplicate sends
  "tracking": true                       // optional — default true (open + click tracking)
}

Success response

200 OK
{
  "id": "uuid",                          // Sendinel email log ID
  "provider_message_id": "re_xxxx",     // Resend (or provider) message ID
  "to": "user@example.com",
  "status": "sent"
}

Examples

curl
curl -X POST https://sendinel.ai/api/v1/send \
  -H "Authorization: Bearer snk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "alex@acme.com",
    "site_id": "your-site-uuid",
    "subject": "Your order is confirmed",
    "html": "<h1>Order #123</h1><p>Ships tomorrow.</p>",
    "idempotency_key": "order-123"
  }'
TypeScript (SDK)
await sendinel.send({
  to: "alex@acme.com",
  siteId: "your-site-uuid",
  subject: "Your order is confirmed",
  html: "<h1>Order #123</h1><p>Ships tomorrow.</p>",
  idempotencyKey: "order-123",
});

Tracking: Open tracking (1x1 pixel) and click tracking (link wrapping) are automatically injected into the HTML body. Set tracking: false to disable.

Conversion Tracking

POST /api/v1/conversion

Track conversions and attribute revenue to email campaigns. Revenue is attributed to the most recent email sent to the contact within a 7-day window. Authenticated via API key with write scope.

Request body

application/json
{
  "email": "user@example.com",       // required
  "event": "purchase",               // optional — default: "purchase"
  "revenue": 49.99,                  // optional
  "currency": "USD",                 // optional — default: "USD"
  "order_id": "order_123",           // optional — deduplication key (409 on duplicate)
  "metadata": { "plan": "pro" }      // optional
}

Success response

200 OK
{
  "id": "uuid",
  "contact_id": "uuid",
  "email_log_id": "uuid",
  "campaign_id": "uuid",
  "event": "purchase",
  "revenue": 49.99,
  "attributed_at": "2026-04-10T12:00:00Z"
}

Attribution logic

Sendinel attributes revenue to the most recent email click within 72 hours, falling back to the most recent open within 72 hours. Revenue data appears in the dashboard overview automatically.

If order_id is provided, duplicate conversions with the same order ID are rejected with 409 Conflict.

Webhooks

Sendinel processes delivery events from your email provider to update status, log opens and clicks, and handle bounces and complaints automatically.

Setup

In your Resend dashboard, add a webhook pointing to:

Code
https://sendinel.ai/api/webhooks/resend

For per-project routing (BYOD with multiple projects):

Code
https://sendinel.ai/api/webhooks/resend/[projectId]

All webhook payloads are verified using Svix signature verification. Set RESEND_WEBHOOK_SECRET in your environment. Per-project secrets are supported and stored encrypted.

Supported events

EventDescriptionAction
email.deliveredEmail accepted by recipient serverUpdates email_log status → 'delivered'
email.openedRecipient opened the emailUpdates status → 'opened', increments open count
email.clickedRecipient clicked a tracked linkUpdates status → 'clicked', logs URL in clicked_urls
email.bouncedHard or soft bounceUpdates status → 'bounced', adds suppression record
email.complainedRecipient marked as spamUpdates status → 'complained', adds suppression, unsubscribes contact

Bounces and complaints trigger Slack alerts if configured. Failed webhook processing is retried via a dead letter queue with exponential backoff (up to 5 attempts).

MCP Tools Reference

Sendinel ships an MCP (Model Context Protocol) server. Every campaign, contact, segment, analytics, and template operation is accessible to AI agents — list campaigns, add contacts, generate emails, check deliverability, approve drafts, and more without writing a line of code.

Three connection methods

TransportBest forHow
Remote HTTP + OAuthClaude.ai, Claude Code, Cursor, Windsurf — zero configclaude mcp add sendinel --url https://sendinel.ai/mcp — browser OAuth flow, no API key needed
stdioLocal clients with manual config — Claude Desktopnpx @sendinel/mcp-server — inject SENDINEL_API_KEY env var
REST/OpenAPINon-MCP agents, web apps, custom integrations/api/mcp/tools + /api/mcp/openapi.json — Bearer API key auth

OAuth discovery

EndpointPurpose
/.well-known/oauth-authorization-serverRFC 8414 discovery — MCP clients find all OAuth endpoints automatically
/.well-known/oauth-protected-resourcePoints clients to the authorization server
/oauth/authorizeBrowser approval page — shows client name, project, and requested scopes
/oauth/tokenExchange auth code for access + refresh tokens (90 day / 1 year)
/oauth/registerDynamic client registration (RFC 7591) — any MCP client can self-register
/oauth/revokeRevoke a token immediately

Available tool groups

GroupToolsDescription
analytics16Stats, domain health, engagement insights, deliverability checks, performance reports
campaigns25Create, clone, enroll, launch, generate email, validate content, test sends
contacts24CRUD, import, merge, tags, segments, suppressions
segments6Create, update, delete, preview; natural language segment builder
drafts6Create, list, approve, reject AI-generated drafts
sites5Create and update sending sites
gdpr2Delete contact data (Article 17), list deletion log

API keys can be scoped to specific tool groups. Destructive operations use a two-call confirmation pattern.

Tool catalog

ToolGroupRequired arguments
abuse_monitor_overrideanalyticssiteId, reason, ttlHours
abuse_monitor_statusanalyticssiteId
create_social_campaignanalyticsname, platform, destination_url
deliverability_checkanalyticssite_id
get_domain_healthanalyticssite_id
get_engagement_insightsanalytics-
get_portfolio_analyticsanalytics-
get_portfolio_statsanalytics-
get_site_insightsanalyticssite_id
get_sitesanalytics-
get_statsanalyticssite_id, start_date
list_event_loganalytics-
list_projectsanalytics-
list_social_campaignsanalytics-
performance_reportanalytics-
schedule_social_postanalyticscampaign_id, content, publisher
add_campaign_stepcampaignscampaign_id, step_order, subject
clone_campaigncampaignscampaign_id, new_name
create_campaigncampaignssite_id, name, type
create_campaign_with_contentcampaignssite_id, name, type, steps
delete_campaigncampaignscampaign_id
delete_campaign_stepcampaignsstep_id
enroll_contactcampaignscampaign_id, contact_id
enroll_segmentcampaignscampaign_id, site_id
generate_emailcampaignssite_id, brief
get_campaigncampaignscampaign_id
get_campaign_calendarcampaignsyear, month
get_enrollment_statuscampaignscampaign_id, contact_id
get_schedulecampaignssite_id
launch_campaigncampaignsname, site_id, emails
list_campaign_stepscampaignscampaign_id
list_campaignscampaigns-
preview_campaign_audiencecampaignscampaign_id
preview_emailcampaigns-
send_test_emailcampaignsto_email, site_id
send_transactionalcampaignsto, site_id, subject
unenroll_contactcampaignscampaign_id, contact_id
update_campaigncampaignscampaign_id
update_campaign_statuscampaignscampaign_id, status
update_campaign_stepcampaignsstep_id
validate_templatecampaignshtml
add_subscribercontactsemail, site_id
add_suppressioncontactsemail
estimate_segment_sizecontacts-
find_duplicatescontacts-
get_contact_schemacontactssite_id
get_hygiene_configcontacts-
get_subscribercontacts-
get_subscriber_timelinecontactssubscriber_id
identify_inactivecontacts-
import_subscriberscontactscontacts, site_id
list_email_logcontacts-
list_hygiene_auditcontactssiteId
list_subscriberscontacts-
list_subscribers_by_segmentcontacts-
list_suppressionscontacts-
list_win_back_draftcontactssiteId
merge_subscriberscontactstarget_id, source_ids
remove_suppressioncontactsemail
run_list_hygienecontacts-
set_subscriber_tagscontactscontact_ids
subscriber_exportcontactscontactId
unsubscribe_subscribercontactsemail
update_hygiene_configcontacts-
update_subscribercontactscontact_id
create_segmentsegmentsname, rules
create_segment_nlsegmentsname, description
delete_segmentsegmentssegment_id
list_segmentssegments-
preview_segmentsegments-
update_segmentsegmentssegment_id
approve_draftdraftsdraft_id
create_draftdraftssite_id, subject, body_html
get_draftdraftsdraft_id
list_draftsdrafts-
promote_draft_to_campaigndraftsdraft_id
reject_draftdraftsdraft_id
create_sendersitesname, slug, from_name
create_sitesitesname, slug, from_name
get_brand_voicesitessite_id
update_brand_voicesitessite_id, brand_voice
update_sitesitessite_id
delete_subscriber_datagdprcontact_id
list_deletion_loggdpr-
check_ab_significanceab-testingtest_id
create_ab_testab-testingcampaign_id, name
list_ab_testsab-testing-
promote_ab_winnerab-testingtest_id
campaign_advisoradvisor-
optimize_subject_linesadvisorcampaign_id, site_id
recovery_planadvisorsite_id
run_portfolio_analysisadvisor-
suggest_content_topicsadvisorsite_id
suggest_send_timeadvisorsite_id
delete_webhook_subscriptionwebhookssubscription_id
list_webhook_subscriptionswebhooks-
subscribe_webhookwebhooksurl, events
update_webhook_subscriptionwebhookssubscription_id
check_approvalapprovalsapproval_id
list_pending_approvalsapprovals-
enroll_automationautomationssubscriber_id
list_automationsautomations-
preview_automationautomationssource_id
trigger_automationautomationssource_id
create_templatetemplatesname, brief
delete_templatetemplatesid
get_templatetemplatesid
list_templatestemplates-
translate_templatetemplates-
update_templatetemplatesid
dnsbl_checkdelivery-opssiteId
dnsbl_delisting_draftdelivery-opssiteId, zone
explain_contact_scoredelivery-ops-
export_datadelivery-opsresource
get_cron_runsdelivery-ops-
get_queue_statusdelivery-ops-
get_scoring_rulesdelivery-ops-
get_send_dlqdelivery-ops-
list_domainsdelivery-ops-
refresh_domain_dnsdelivery-opsdomain_id
register_domaindelivery-opssite_id, domain
update_scoring_rulesdelivery-ops-
create_template_from_briefcompoundsite_id, name, brief
diagnose_delivery_issuecompound-
onboard_new_sitecompoundname, domain
setup_campaign_from_briefcompoundsite_id, campaign_name, brief
configure_platform_triggerdataplatform, event_type, action
connect_platformdataplatform, credentials
create_api_keydataname
disconnect_platformdata-
list_api_keysdata-
list_connected_platformsdata-
list_import_historydata-
list_notificationsdata-
list_platform_eventsdata-
mark_notifications_readdata-
migrate_campaigns_fromdataprovider, oauth_token
revoke_api_keydatakey_id
start_platform_migrationdataconnection_id
tenant_export_fulldatasiteId
create_content_blockcontentname, html
delete_content_blockcontentblock_id
list_content_blockscontent-
list_content_sourcescontent-
trigger_content_pipelinecontent-
get_warmup_statuswarmupsite_id
pause_warmupwarmupsite_id
resume_warmupwarmupsite_id
update_warmup_schedulewarmupsite_id
create_formformssite_id, name, fields
get_form_statsformsform_id
list_formsforms-
get_plan_limitsorg-
get_plan_usageorg-
invite_team_memberorgemail
list_team_membersorg-
get_sms_provider_statussms-
list_sms_inboundsms-
list_sms_logsms-
preview_sms_costsmsbody
send_smssmsbody
send_sms_campaignsmssite_id, name, body
set_contact_sms_consentsmscontact_id, status
adapt_social_postsocial-postsemail_campaign_id, platforms
create_social_postsocial-poststitle, content, platforms
delete_social_postsocial-postspost_id
get_social_postsocial-postspost_id
list_social_postssocial-posts-
update_social_postsocial-postspost_id

AI Clients

Connect Sendinel to any MCP-compatible AI client. Once connected, your agent can manage your email operations conversationally through the MCP tool catalog.

claude_desktop_config.json

Open Settings -> Developer -> Edit Config, then add this local MCP server. Use this when you normally work in Git Bash but the desktop AI client is launched by Windows.

{
  "mcpServers": {
    "sendinel": {
      "command": "C:\\Program Files\\Git\\bin\\bash.exe",
      "args": [
        "-lc",
        "npx -y @sendinel/mcp-server@latest"
      ],
      "env": {
        "SENDINEL_API_KEY": "snk_your_api_key"
      }
    }
  }
}

Example agent conversation

MCP tool call
// Your prompt:
"List my active campaigns and tell me which ones have the lowest open rates."

// Agent calls:
list_campaigns({ "status": "active" })
get_stats({ "campaign_id": "uuid", "period": "30d" })

// Returns:
{
  "campaigns": [
    { "name": "Welcome Series", "open_rate": 0.42, "enrolled": 142 },
    { "name": "Re-engagement",  "open_rate": 0.11, "enrolled": 38 }
  ]
}

Delivery Semantics

Email delivery is asynchronous infrastructure, not a synchronous HTTP side effect. Sendinel accepts API requests, applies safety policy, queues work, and then workers send through the configured provider. This section explains what the API response means and why a message may wait before leaving the queue.

StateMeaningCommon reason
acceptedSendinel validated the request and created queue/log recordsNormal response from API or scheduler
queuedMessage is waiting for an eligible worker slotBatch limits, warmup cap, send window, blackout date
sentProvider accepted the message for deliveryResend/Mailgun/SES accepted the API call
deliveredRecipient server accepted the messageProvider webhook received
opened / clickedRecipient engagement was trackedTracking pixel or wrapped link fired
bounced / complainedRecipient server rejected or user reported spamContact is suppressed automatically

What “sent” means

sent means the sending provider accepted the message from Sendinel. It does not mean the recipient inbox accepted it. Final delivery, bounce, complaint, open, and click states arrive later through provider webhooks.

Why sends wait in queue

ControlEffect
Warmup capsNew or recently migrated domains may be limited to a small daily volume until trust is established.
Provider warmupIf the email provider also reports a dedicated-IP warmup cap, Sendinel uses the lower of both limits.
Plan batch sizeWorker batch sizes scale by plan tier; larger plans drain queues faster.
Send windowsScheduled sends only fire inside the configured allowed days and times.
Blackout datesCampaign sends pause on configured blackout days.
Suppression checksBounced, complained, unsubscribed, or manually suppressed contacts are skipped before provider send.
IdempotencyDuplicate transactional requests with the same idempotency key return the existing result instead of sending twice.
If a broadcast appears “stuck,” check the selected sending domain’s warmup status, daily cap, send window, blackout dates, and queue status before retrying. Retrying without an idempotency key can create duplicate work.

Rate Limits & Plans

Rate limits are enforced per project using a sliding window. Limits vary by plan and operation type.

PlanSend APIRead endpointsWrite endpoints
Free10 req / 5 min60 req / 5 min30 req / 5 min
BYOD100 req / 5 min300 req / 5 min150 req / 5 min
Managed500 req / 5 min600 req / 5 min200 req / 5 min
Agency Plus1000 req / 5 min1200 req / 5 min400 req / 5 min
Enterprise1000 req / 5 min1200 req / 5 min400 req / 5 min

Rate limit headers

Code
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1744329600

When rate limited, the API returns 429 Too Many Requests with a Retry-After header. The SDK handles 429s automatically with backoff and retry.

Error Codes

All error responses return a JSON body with an error field describing the issue.

StatusMeaningCommon causes
400Bad RequestMissing required fields, invalid email format, body too large
401UnauthorizedMissing or invalid API key, expired key
403ForbiddenAPI key lacks required scope (write scope needed for mutations)
404Not FoundInvalid site_id or resource ID
409ConflictDuplicate idempotency_key (email already sent), duplicate order_id
422UnprocessableContact limit reached — cannot auto-create contact (track endpoint)
429Rate LimitedToo many requests in the current window
500Server ErrorInternal error — retry with exponential backoff
Error response shape
{
  "error": "Descriptive error message",
  "code": "SUPPRESSED_CONTACT",   // optional — machine-readable code
  "details": { ... }              // optional — additional context
}

Need help? support@sendinel.ai