Home

Nitrosend API

v1.1.2
Base URLs
https://api.nitrosend.comProduction
http://localhost:8000Local development

Multi-channel marketing automation API. Send email campaigns, build automation flows, manage contacts and segments, track events, and configure your Brand Kit — all via a single REST API.

Authentication

All /v1/my/* endpoints require authentication via Bearer token. Two token types are accepted (tried in order):

  1. API KeyAuthorization: Bearer nskey_live_...
  2. JWTAuthorization: Bearer <jwt> (obtained from POST /v1/login)

Pagination

Paginated endpoints return these headers:

  • X-Total-Count — total records
  • X-Total-Pages — total pages
  • X-Page-Number — current page
  • X-Next-Page — next page (omitted on last page)
  • X-Prev-Page — previous page (omitted on first page)

Use page and limit (or per) query params to control pagination. Maximum limit is 100, default is 30.

Error Responses

All errors return a consistent JSON shape:

{
  "code": 422,
  "message": "Description of error",
  "error": true,
  "validation_errors": { "field": ["error message"] }
}

The validation_errors key is only present on 422 responses.

Authentication

BearerAuthhttp

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Scheme: bearer

Auth

Login, logout, and registration

Create a new account

POST
https://api.nitrosend.com/v1/signup

Body

application/json
userobjectrequired
Show child attributes
first_namestring
last_namestring
emailstring<email>required
mobilestring
invite_tokenstring | null
passwordstring<password>required
password_confirmationstring<password>

Response

201CreatedUser

Account created

422Unprocessable EntityError & object

Validation failed

Create a new account
curl -X POST 'https://api.nitrosend.com/v1/signup' \
  -H 'Content-Type: application/json' \
  -d '{
    "user": {
      "first_name": "string",
      "last_name": "string",
      "email": "user@example.com",
      "mobile": "string",
      "invite_token": "string",
      "password": "********",
      "password_confirmation": "********"
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/signup', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "user": {
        "first_name": "string",
        "last_name": "string",
        "email": "user@example.com",
        "mobile": "string",
        "invite_token": "string",
        "password": "********",
        "password_confirmation": "********"
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "user": {
    "first_name": "string",
    "last_name": "string",
    "email": "user@example.com",
    "mobile": "string",
    "invite_token": "string",
    "password": "********",
    "password_confirmation": "********"
  }
}

response = requests.post('https://api.nitrosend.com/v1/signup', json=payload)
data = response.json()
Request Body
{
  "user": {
    "first_name": "string",
    "last_name": "string",
    "email": "user@example.com",
    "mobile": "string",
    "invite_token": "string",
    "password": "********",
    "password_confirmation": "********"
  }
}
{
  "id": 0,
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "country_code": "string",
  "time_zone": "string",
  "admin": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Sign in and receive a JWT

POST
https://api.nitrosend.com/v1/login

Body

application/json
userobjectrequired
Show child attributes
emailstring<email>required
passwordstring<password>required

Response

200OKUser

Signed in

401UnauthorizedError

Not authenticated

Sign in and receive a JWT
curl -X POST 'https://api.nitrosend.com/v1/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "user": {
      "email": "user@example.com",
      "password": "********"
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "user": {
        "email": "user@example.com",
        "password": "********"
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "user": {
    "email": "user@example.com",
    "password": "********"
  }
}

response = requests.post('https://api.nitrosend.com/v1/login', json=payload)
data = response.json()
Request Body
{
  "user": {
    "email": "user@example.com",
    "password": "********"
  }
}
{
  "id": 0,
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "country_code": "string",
  "time_zone": "string",
  "admin": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Sign out and revoke JWT

DELETE
https://api.nitrosend.com/v1/logout

Response

204No Content

Signed out

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Sign out and revoke JWT
curl -X DELETE 'https://api.nitrosend.com/v1/logout'
const response = await fetch('https://api.nitrosend.com/v1/logout', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/logout')
data = response.json()

Get OAuth popup context

GET
https://api.nitrosend.com/v1/oauth/me

Returns the account-selection and subscription context used by the /oauth/connect popup. This endpoint accepts the normal Bearer JWT and, for popup resume flows, the authenticated browser session cookie established by Devise/OmniAuth.

Response

200OKOAuthPopupContext

Popup context

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get OAuth popup context
curl -X GET 'https://api.nitrosend.com/v1/oauth/me'
const response = await fetch('https://api.nitrosend.com/v1/oauth/me', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/oauth/me')
data = response.json()
{
  "accounts": [
    {
      "id": 0,
      "name": "string",
      "can_manage": true,
      "needs_subscribe": true
    }
  ],
  "plans": [
    {
      "id": 0,
      "name": "string",
      "slug": "string",
      "base_price_cents": 0,
      "interval": "string"
    }
  ],
  "stripe_publishable_key": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Subscribe the selected OAuth popup account

POST
https://api.nitrosend.com/v1/oauth/subscribe

Finalizes plan selection for the /oauth/connect popup before the browser returns to the Doorkeeper consent screen. This endpoint accepts the normal Bearer JWT and, for popup resume flows, the authenticated browser session cookie established by Devise/OmniAuth.

Body

application/json
plan_idintegerrequired
account_idinteger | null
stripe_tokenstring | null

Response

200OKOAuthPopupSubscribeResponse

Popup can continue to consent

401UnauthorizedError

Not authenticated

422Unprocessable Entityobject

Account or plan selection failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Subscribe the selected OAuth popup account
curl -X POST 'https://api.nitrosend.com/v1/oauth/subscribe' \
  -H 'Content-Type: application/json' \
  -d '{
    "plan_id": 0,
    "account_id": 0,
    "stripe_token": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/oauth/subscribe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "plan_id": 0,
      "account_id": 0,
      "stripe_token": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "plan_id": 0,
  "account_id": 0,
  "stripe_token": "string"
}

response = requests.post('https://api.nitrosend.com/v1/oauth/subscribe', json=payload)
data = response.json()
Request Body
{
  "plan_id": 0,
  "account_id": 0,
  "stripe_token": "string"
}
{
  "next_step": "consent"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "error": "string"
}

Create a CSRF-safe OAuth provider launch URL

POST
https://api.nitrosend.com/v1/oauth/launch

Mints a short-lived launch URL for Google or GitHub sign-in. The frontend follows the returned API-origin launch page, which renders the real POST form to /auth/:provider with a valid Rails authenticity token. This preserves OmniAuth's POST-only request phase and CSRF protection for both the app popup flow and the /oauth/connect agent popup flow.

Body

application/json
providerstringgoogle_oauth2githubrequired
auth_intentstringappagentapp
auth_stepstringloginsignuplogin
resume_urlstring<uri> | null

Required when auth_intent=agent; must point to the frontend /oauth/connect route.

Response

200OKOAuthLaunchResponse

Launch URL created

400Bad Requestobject

Invalid provider or resume URL

Create a CSRF-safe OAuth provider launch URL
curl -X POST 'https://api.nitrosend.com/v1/oauth/launch' \
  -H 'Content-Type: application/json' \
  -d '{
    "provider": "google_oauth2",
    "auth_intent": "app",
    "auth_step": "login",
    "resume_url": "https://example.com"
  }'
const response = await fetch('https://api.nitrosend.com/v1/oauth/launch', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "provider": "google_oauth2",
      "auth_intent": "app",
      "auth_step": "login",
      "resume_url": "https://example.com"
    }),
});

const data = await response.json();
import requests

payload = {
  "provider": "google_oauth2",
  "auth_intent": "app",
  "auth_step": "login",
  "resume_url": "https://example.com"
}

response = requests.post('https://api.nitrosend.com/v1/oauth/launch', json=payload)
data = response.json()
Request Body
{
  "provider": "google_oauth2",
  "auth_intent": "app",
  "auth_step": "login",
  "resume_url": "https://example.com"
}
{
  "launch_url": "https://example.com"
}
{
  "error": "string"
}

User

Current user profile

Get current user profile

GET
https://api.nitrosend.com/v1/my/user

Response

200OKUser

User profile

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get current user profile
curl -X GET 'https://api.nitrosend.com/v1/my/user'
const response = await fetch('https://api.nitrosend.com/v1/my/user', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/user')
data = response.json()
{
  "id": 0,
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "country_code": "string",
  "time_zone": "string",
  "admin": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update current user profile

PATCH
https://api.nitrosend.com/v1/my/user

Body

application/json
first_namestring
last_namestring
emailstring<email>
mobilestring
time_zonestring | null

Response

200OKUser

Updated user

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update current user profile
curl -X PATCH 'https://api.nitrosend.com/v1/my/user' \
  -H 'Content-Type: application/json' \
  -d '{
    "first_name": "string",
    "last_name": "string",
    "email": "user@example.com",
    "mobile": "string",
    "time_zone": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/user', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "first_name": "string",
      "last_name": "string",
      "email": "user@example.com",
      "mobile": "string",
      "time_zone": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "time_zone": "string"
}

response = requests.patch('https://api.nitrosend.com/v1/my/user', json=payload)
data = response.json()
Request Body
{
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "time_zone": "string"
}
{
  "id": 0,
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "country_code": "string",
  "time_zone": "string",
  "admin": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get current impersonation status

GET
https://api.nitrosend.com/v1/my/impersonation

Response

200OKImpersonationStatus

Current impersonation state

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get current impersonation status
curl -X GET 'https://api.nitrosend.com/v1/my/impersonation'
const response = await fetch('https://api.nitrosend.com/v1/my/impersonation', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/impersonation')
data = response.json()
{
  "impersonating": true,
  "impersonator": {
    "id": 0,
    "email": "user@example.com",
    "name": "string"
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Revoke the active impersonation session

DELETE
https://api.nitrosend.com/v1/my/impersonation

Response

200OKImpersonationExit

Impersonation session revoked

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Revoke the active impersonation session
curl -X DELETE 'https://api.nitrosend.com/v1/my/impersonation'
const response = await fetch('https://api.nitrosend.com/v1/my/impersonation', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/impersonation')
data = response.json()
{
  "redirect_url": "https://api.nitrosend.com/adm"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Account

Account settings and configuration

Get account details

GET
https://api.nitrosend.com/v1/my/account

Response

200OKAccount

Account with brands and billing

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get account details
curl -X GET 'https://api.nitrosend.com/v1/my/account'
const response = await fetch('https://api.nitrosend.com/v1/my/account', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/account')
data = response.json()
200
{
  "id": 0,
  "name": "string",
  "avatar": "string",
  "banner": "string",
  "account_tier": "free",
  "safe_mode_enabled": true,
  "billing": {
    "plan_name": "string",
    "overage": {},
    "resources": {
      "email": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "sms": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "ai": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      }
    },
    "brands": {
      "used": 0,
      "limit": 0,
      "remaining": 0,
      "unlimited": true,
      "can_create": true
    },
    "lifetime": {
      "email_sent": 0,
      "sms_sent": 0,
      "ai_used": 0
    }
  },
  "brands": [
    {
      "id": 0,
      "sid": "string",
      "account_id": 0,
      "brand_color": "string",
      "text_color": "string",
      "bg_color": "string",
      "radius": 0,
      "spacing_density": "compact",
      "font_heading": "string",
      "font_body": "string",
      "brand_document": "string",
      "style_notes": "string",
      "tone": "string",
      "company_description": "string",
      "industry": "string",
      "example_copy": [
        "string"
      ],
      "default_header": {},
      "default_footer": {},
      "default_theme": {},
      "physical_address": "string",
      "company_name": "string",
      "source_url": "string",
      "last_scraped_at": "2024-01-15T09:30:00Z",
      "links": [
        {
          "url": "string",
          "icon": "string",
          "title": "string"
        }
      ],
      "logo": "string",
      "complete": true,
      "email_from_name": "string",
      "email_from_email": "string",
      "email_reply_to": "string",
      "email_view_online": true,
      "test_email_recipients": [
        "user@example.com"
      ],
      "onboarding_state": {},
      "onboarding": {
        "steps": {},
        "progress": {
          "completed": 0,
          "total": 0
        }
      },
      "domain_verified": true,
      "can_send": true,
      "byo_routing": {
        "mismatch": true,
        "provider": "string",
        "bypassing_domains": [
          "string"
        ],
        "message": "string"
      },
      "subscribed_contacts_count": 0,
      "using_sandbox": true,
      "sandbox_email": "string",
      "sandbox_monthly_cap": 0,
      "sandbox_sends_remaining": 0,
      "capabilities": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "team": {
    "seat_limit": 0,
    "seat_count": 0,
    "member_count": 0,
    "invite_count": 0,
    "current_role": "string",
    "can_manage_team": true
  },
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Update account settings

PATCH
https://api.nitrosend.com/v1/my/account

Body

application/json
namestring
avatarstring

Signed blob ID

bannerstring

Signed blob ID

Response

200OKAccount

Updated account

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update account settings
curl -X PATCH 'https://api.nitrosend.com/v1/my/account' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "avatar": "string",
    "banner": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/account', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "avatar": "string",
      "banner": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "avatar": "string",
  "banner": "string"
}

response = requests.patch('https://api.nitrosend.com/v1/my/account', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "avatar": "string",
  "banner": "string"
}
{
  "id": 0,
  "name": "string",
  "avatar": "string",
  "banner": "string",
  "account_tier": "free",
  "safe_mode_enabled": true,
  "billing": {
    "plan_name": "string",
    "overage": {},
    "resources": {
      "email": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "sms": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "ai": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      }
    },
    "brands": {
      "used": 0,
      "limit": 0,
      "remaining": 0,
      "unlimited": true,
      "can_create": true
    },
    "lifetime": {
      "email_sent": 0,
      "sms_sent": 0,
      "ai_used": 0
    }
  },
  "brands": [
    {
      "id": 0,
      "sid": "string",
      "account_id": 0,
      "brand_color": "string",
      "text_color": "string",
      "bg_color": "string",
      "radius": 0,
      "spacing_density": "compact",
      "font_heading": "string",
      "font_body": "string",
      "brand_document": "string",
      "style_notes": "string",
      "tone": "string",
      "company_description": "string",
      "industry": "string",
      "example_copy": [
        "string"
      ],
      "default_header": {},
      "default_footer": {},
      "default_theme": {},
      "physical_address": "string",
      "company_name": "string",
      "source_url": "string",
      "last_scraped_at": "2024-01-15T09:30:00Z",
      "links": [
        {
          "url": "string",
          "icon": "string",
          "title": "string"
        }
      ],
      "logo": "string",
      "complete": true,
      "email_from_name": "string",
      "email_from_email": "string",
      "email_reply_to": "string",
      "email_view_online": true,
      "test_email_recipients": [
        "user@example.com"
      ],
      "onboarding_state": {},
      "onboarding": {
        "steps": {},
        "progress": {
          "completed": 0,
          "total": 0
        }
      },
      "domain_verified": true,
      "can_send": true,
      "byo_routing": {
        "mismatch": true,
        "provider": "string",
        "bypassing_domains": [
          "string"
        ],
        "message": "string"
      },
      "subscribed_contacts_count": 0,
      "using_sandbox": true,
      "sandbox_email": "string",
      "sandbox_monthly_cap": 0,
      "sandbox_sends_remaining": 0,
      "capabilities": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "team": {
    "seat_limit": 0,
    "seat_count": 0,
    "member_count": 0,
    "invite_count": 0,
    "current_role": "string",
    "can_manage_team": true
  },
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

List accessible accounts (paginated)

GET
https://api.nitrosend.com/v1/my/accounts

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<Account>

Accessible accounts

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List accessible accounts (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/accounts'
const response = await fetch('https://api.nitrosend.com/v1/my/accounts', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/accounts')
data = response.json()
200
[
  {
    "id": 0,
    "name": "string",
    "avatar": "string",
    "banner": "string",
    "account_tier": "free",
    "safe_mode_enabled": true,
    "billing": {
      "plan_name": "string",
      "overage": {},
      "resources": {
        "email": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "sms": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "ai": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        }
      },
      "brands": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "unlimited": true,
        "can_create": true
      },
      "lifetime": {
        "email_sent": 0,
        "sms_sent": 0,
        "ai_used": 0
      }
    },
    "brands": [
      {
        "id": 0,
        "sid": "string",
        "account_id": 0,
        "brand_color": "string",
        "text_color": "string",
        "bg_color": "string",
        "radius": 0,
        "spacing_density": "compact",
        "font_heading": "string",
        "font_body": "string",
        "brand_document": "string",
        "style_notes": "string",
        "tone": "string",
        "company_description": "string",
        "industry": "string",
        "example_copy": [
          "string"
        ],
        "default_header": {},
        "default_footer": {},
        "default_theme": {},
        "physical_address": "string",
        "company_name": "string",
        "source_url": "string",
        "last_scraped_at": "2024-01-15T09:30:00Z",
        "links": [
          {
            "url": "string",
            "icon": "string",
            "title": "string"
          }
        ],
        "logo": "string",
        "complete": true,
        "email_from_name": "string",
        "email_from_email": "string",
        "email_reply_to": "string",
        "email_view_online": true,
        "test_email_recipients": [
          "user@example.com"
        ],
        "onboarding_state": {},
        "onboarding": {
          "steps": {},
          "progress": {
            "completed": 0,
            "total": 0
          }
        },
        "domain_verified": true,
        "can_send": true,
        "byo_routing": {
          "mismatch": true,
          "provider": "string",
          "bypassing_domains": [
            "string"
          ],
          "message": "string"
        },
        "subscribed_contacts_count": 0,
        "using_sandbox": true,
        "sandbox_email": "string",
        "sandbox_monthly_cap": 0,
        "sandbox_sends_remaining": 0,
        "capabilities": {},
        "created_at": "2024-01-15T09:30:00Z",
        "updated_at": "2024-01-15T09:30:00Z"
      }
    ],
    "team": {
      "seat_limit": 0,
      "seat_count": 0,
      "member_count": 0,
      "invite_count": 0,
      "current_role": "string",
      "can_manage_team": true
    },
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Get team metadata for the current account

GET
https://api.nitrosend.com/v1/my/account/team

Response

200OKAccountTeam

Team metadata

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get team metadata for the current account
curl -X GET 'https://api.nitrosend.com/v1/my/account/team'
const response = await fetch('https://api.nitrosend.com/v1/my/account/team', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/account/team')
data = response.json()
200
{
  "account": {
    "id": 0,
    "name": "string",
    "avatar": "string",
    "banner": "string",
    "account_tier": "free",
    "safe_mode_enabled": true,
    "billing": {
      "plan_name": "string",
      "overage": {},
      "resources": {
        "email": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "sms": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "ai": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        }
      },
      "brands": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "unlimited": true,
        "can_create": true
      },
      "lifetime": {
        "email_sent": 0,
        "sms_sent": 0,
        "ai_used": 0
      }
    },
    "brands": [
      {
        "id": 0,
        "sid": "string",
        "account_id": 0,
        "brand_color": "string",
        "text_color": "string",
        "bg_color": "string",
        "radius": 0,
        "spacing_density": "compact",
        "font_heading": "string",
        "font_body": "string",
        "brand_document": "string",
        "style_notes": "string",
        "tone": "string",
        "company_description": "string",
        "industry": "string",
        "example_copy": [
          "string"
        ],
        "default_header": {},
        "default_footer": {},
        "default_theme": {},
        "physical_address": "string",
        "company_name": "string",
        "source_url": "string",
        "last_scraped_at": "2024-01-15T09:30:00Z",
        "links": [
          {
            "url": "string",
            "icon": "string",
            "title": "string"
          }
        ],
        "logo": "string",
        "complete": true,
        "email_from_name": "string",
        "email_from_email": "string",
        "email_reply_to": "string",
        "email_view_online": true,
        "test_email_recipients": [
          "user@example.com"
        ],
        "onboarding_state": {},
        "onboarding": {
          "steps": {},
          "progress": {
            "completed": 0,
            "total": 0
          }
        },
        "domain_verified": true,
        "can_send": true,
        "byo_routing": {
          "mismatch": true,
          "provider": "string",
          "bypassing_domains": [
            "string"
          ],
          "message": "string"
        },
        "subscribed_contacts_count": 0,
        "using_sandbox": true,
        "sandbox_email": "string",
        "sandbox_monthly_cap": 0,
        "sandbox_sends_remaining": 0,
        "capabilities": {},
        "created_at": "2024-01-15T09:30:00Z",
        "updated_at": "2024-01-15T09:30:00Z"
      }
    ],
    "team": {
      "seat_limit": 0,
      "seat_count": 0,
      "member_count": 0,
      "invite_count": 0,
      "current_role": "string",
      "can_manage_team": true
    },
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "memberships": [
    {
      "id": 0,
      "role": "member",
      "user_id": 0,
      "email": "user@example.com",
      "name": "string",
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "invites": [
    {
      "id": 0,
      "account_id": 0,
      "email": "user@example.com",
      "role": "member",
      "token": "string",
      "status": "pending",
      "accepted_at": "2024-01-15T09:30:00Z",
      "revoked_at": "2024-01-15T09:30:00Z",
      "expires_at": "2024-01-15T09:30:00Z",
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "accessible_accounts": [
    {
      "id": 0,
      "name": "string",
      "avatar": "string",
      "banner": "string",
      "account_tier": "free",
      "safe_mode_enabled": true,
      "billing": {
        "plan_name": "string",
        "overage": {},
        "resources": {
          "email": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          },
          "sms": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          },
          "ai": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          }
        },
        "brands": {
          "used": 0,
          "limit": 0,
          "remaining": 0,
          "unlimited": true,
          "can_create": true
        },
        "lifetime": {
          "email_sent": 0,
          "sms_sent": 0,
          "ai_used": 0
        }
      },
      "brands": [
        {
          "id": 0,
          "sid": "string",
          "account_id": 0,
          "brand_color": "string",
          "text_color": "string",
          "bg_color": "string",
          "radius": 0,
          "spacing_density": "compact",
          "font_heading": "string",
          "font_body": "string",
          "brand_document": "string",
          "style_notes": "string",
          "tone": "string",
          "company_description": "string",
          "industry": "string",
          "example_copy": [
            "string"
          ],
          "default_header": {},
          "default_footer": {},
          "default_theme": {},
          "physical_address": "string",
          "company_name": "string",
          "source_url": "string",
          "last_scraped_at": "2024-01-15T09:30:00Z",
          "links": [
            {
              "url": "string",
              "icon": "string",
              "title": "string"
            }
          ],
          "logo": "string",
          "complete": true,
          "email_from_name": "string",
          "email_from_email": "string",
          "email_reply_to": "string",
          "email_view_online": true,
          "test_email_recipients": [
            "user@example.com"
          ],
          "onboarding_state": {},
          "onboarding": {
            "steps": {},
            "progress": {
              "completed": 0,
              "total": 0
            }
          },
          "domain_verified": true,
          "can_send": true,
          "byo_routing": {
            "mismatch": true,
            "provider": "string",
            "bypassing_domains": [
              "string"
            ],
            "message": "string"
          },
          "subscribed_contacts_count": 0,
          "using_sandbox": true,
          "sandbox_email": "string",
          "sandbox_monthly_cap": 0,
          "sandbox_sends_remaining": 0,
          "capabilities": {},
          "created_at": "2024-01-15T09:30:00Z",
          "updated_at": "2024-01-15T09:30:00Z"
        }
      ],
      "team": {
        "seat_limit": 0,
        "seat_count": 0,
        "member_count": 0,
        "invite_count": 0,
        "current_role": "string",
        "can_manage_team": true
      },
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

List account memberships (paginated)

GET
https://api.nitrosend.com/v1/my/account/memberships

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<AccountMembership>

Memberships

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List account memberships (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/account/memberships'
const response = await fetch('https://api.nitrosend.com/v1/my/account/memberships', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/account/memberships')
data = response.json()
200
[
  {
    "id": 0,
    "role": "member",
    "user_id": 0,
    "email": "user@example.com",
    "name": "string",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Remove a membership

DELETE
https://api.nitrosend.com/v1/my/account/memberships/{id}

Parameters

idintegerrequiredpath

Response

200OKAccountMembership

Removed membership

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Remove a membership
curl -X DELETE 'https://api.nitrosend.com/v1/my/account/memberships/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/account/memberships/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/account/memberships/{id}')
data = response.json()
200
{
  "id": 0,
  "role": "member",
  "user_id": 0,
  "email": "user@example.com",
  "name": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Update a membership role

PATCH
https://api.nitrosend.com/v1/my/account/memberships/{id}

Body

application/json
rolestringmemberadmin

Parameters

idintegerrequiredpath

Response

200OKAccountMembership

Updated membership

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a membership role
curl -X PATCH 'https://api.nitrosend.com/v1/my/account/memberships/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "role": "member"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/account/memberships/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "role": "member"
    }),
});

const data = await response.json();
import requests

payload = {
  "role": "member"
}

response = requests.patch('https://api.nitrosend.com/v1/my/account/memberships/{id}', json=payload)
data = response.json()
Request Body
{
  "role": "member"
}
{
  "id": 0,
  "role": "member",
  "user_id": 0,
  "email": "user@example.com",
  "name": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

List account invites (paginated)

GET
https://api.nitrosend.com/v1/my/account/invites

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<AccountInvite>

Invites

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List account invites (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/account/invites'
const response = await fetch('https://api.nitrosend.com/v1/my/account/invites', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/account/invites')
data = response.json()
200
[
  {
    "id": 0,
    "account_id": 0,
    "email": "user@example.com",
    "role": "member",
    "token": "string",
    "status": "pending",
    "accepted_at": "2024-01-15T09:30:00Z",
    "revoked_at": "2024-01-15T09:30:00Z",
    "expires_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Create an account invite

POST
https://api.nitrosend.com/v1/my/account/invites

Body

application/json
emailstring<email>required
rolestringmemberadmin

Response

201CreatedAccountInvite

Created invite

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create an account invite
curl -X POST 'https://api.nitrosend.com/v1/my/account/invites' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "user@example.com",
    "role": "member"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/account/invites', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "email": "user@example.com",
      "role": "member"
    }),
});

const data = await response.json();
import requests

payload = {
  "email": "user@example.com",
  "role": "member"
}

response = requests.post('https://api.nitrosend.com/v1/my/account/invites', json=payload)
data = response.json()
Request Body
{
  "email": "user@example.com",
  "role": "member"
}
{
  "id": 0,
  "account_id": 0,
  "email": "user@example.com",
  "role": "member",
  "token": "string",
  "status": "pending",
  "accepted_at": "2024-01-15T09:30:00Z",
  "revoked_at": "2024-01-15T09:30:00Z",
  "expires_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Revoke an invite

DELETE
https://api.nitrosend.com/v1/my/account/invites/{id}

Parameters

idintegerrequiredpath

Response

200OKAccountInvite

Revoked invite

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Revoke an invite
curl -X DELETE 'https://api.nitrosend.com/v1/my/account/invites/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/account/invites/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/account/invites/{id}')
data = response.json()
200
{
  "id": 0,
  "account_id": 0,
  "email": "user@example.com",
  "role": "member",
  "token": "string",
  "status": "pending",
  "accepted_at": "2024-01-15T09:30:00Z",
  "revoked_at": "2024-01-15T09:30:00Z",
  "expires_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Accept an invite token

POST
https://api.nitrosend.com/v1/my/account/invites/{id}/accept

Parameters

idstringrequiredpath

Response

200OKAccountInvite

Accepted invite

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Accept an invite token
curl -X POST 'https://api.nitrosend.com/v1/my/account/invites/{id}/accept'
const response = await fetch('https://api.nitrosend.com/v1/my/account/invites/{id}/accept', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/account/invites/{id}/accept')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "email": "user@example.com",
  "role": "member",
  "token": "string",
  "status": "pending",
  "accepted_at": "2024-01-15T09:30:00Z",
  "revoked_at": "2024-01-15T09:30:00Z",
  "expires_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Billing

Subscription billing and checkout

Create a subscription

POST
https://api.nitrosend.com/v1/my/subscription

Body

application/json
plan_idintegerrequired
stripe_tokenstring | null

Stripe card token for paid-plan creation.

coupon_codestring | null

Customer-entered Stripe promotion code or coupon ID.

Response

201CreatedSubscription

Subscription created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a subscription
curl -X POST 'https://api.nitrosend.com/v1/my/subscription' \
  -H 'Content-Type: application/json' \
  -d '{
    "plan_id": 0,
    "stripe_token": "string",
    "coupon_code": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "plan_id": 0,
      "stripe_token": "string",
      "coupon_code": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "plan_id": 0,
  "stripe_token": "string",
  "coupon_code": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/subscription', json=payload)
data = response.json()
Request Body
{
  "plan_id": 0,
  "stripe_token": "string",
  "coupon_code": "string"
}
{
  "id": 0,
  "plan_id": 0,
  "plan_name": "string",
  "status": "pending",
  "interval": "month",
  "currency": "string",
  "subtotal_cents": 0,
  "tax_cents": 0,
  "total_cents": 0,
  "base_price_cents": 0,
  "discount_cents": 0,
  "next_payment_cents": 0,
  "discount_end_at": "2024-01-15T09:30:00Z",
  "activated": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Change the current subscription plan

PUT
https://api.nitrosend.com/v1/my/subscription/change

Body

application/json
plan_idintegerrequired
coupon_codestring | null

Customer-entered Stripe promotion code or coupon ID.

Response

200OKSubscription

Subscription updated

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Change the current subscription plan
curl -X PUT 'https://api.nitrosend.com/v1/my/subscription/change' \
  -H 'Content-Type: application/json' \
  -d '{
    "plan_id": 0,
    "coupon_code": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/subscription/change', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "plan_id": 0,
      "coupon_code": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "plan_id": 0,
  "coupon_code": "string"
}

response = requests.put('https://api.nitrosend.com/v1/my/subscription/change', json=payload)
data = response.json()
Request Body
{
  "plan_id": 0,
  "coupon_code": "string"
}
{
  "id": 0,
  "plan_id": 0,
  "plan_name": "string",
  "status": "pending",
  "interval": "month",
  "currency": "string",
  "subtotal_cents": 0,
  "tax_cents": 0,
  "total_cents": 0,
  "base_price_cents": 0,
  "discount_cents": 0,
  "next_payment_cents": 0,
  "discount_end_at": "2024-01-15T09:30:00Z",
  "activated": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Preview a promotion code or coupon for a subscription plan

POST
https://api.nitrosend.com/v1/my/subscription/coupon_preview

Validates a customer-entered Stripe promotion code or coupon ID against a paid plan and returns display totals for the checkout summary. The final subscription create/change request must still send coupon_code; preview data is not trusted as payment input.

Body

application/json
plan_idintegerrequired
coupon_codestringrequired

Customer-entered Stripe promotion code or coupon ID.

Response

200OKSubscriptionCouponPreview

Coupon preview

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Preview a promotion code or coupon for a subscription plan
curl -X POST 'https://api.nitrosend.com/v1/my/subscription/coupon_preview' \
  -H 'Content-Type: application/json' \
  -d '{
    "plan_id": 0,
    "coupon_code": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/subscription/coupon_preview', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "plan_id": 0,
      "coupon_code": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "plan_id": 0,
  "coupon_code": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/subscription/coupon_preview', json=payload)
data = response.json()
Request Body
{
  "plan_id": 0,
  "coupon_code": "string"
}
{
  "code": "string",
  "discount_label": "string",
  "discount_cents": 0,
  "subtotal_cents": 0,
  "tax_cents": 0,
  "total_cents": 0,
  "total_after_discount_cents": 0,
  "currency": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Contacts

Contact management

List contacts (paginated)

GET
https://api.nitrosend.com/v1/my/contacts

Parameters

pageinteger1query
limitinteger<= 10050query
searchstringquery

Full-text search across name, email, phone

filtersSegmentFilterExpressionquery

Canonical structured audience filters from the registry exposed by /v1/my/flows/spec and nitro://schema. search remains a separate full-text lookup; filters are applied through the same fail-closed validator used for segments.

list_idintegerquery

Legacy shortcut for a contact_list in [id] filter.

tagstringquery

Legacy shortcut for a contact_tag eq tag filter. Tags are stored as an array of strings under data.tags. For richer tag targeting, use canonical filters.

sortstringcreated_atemails_sentunique_opensclicksopen_rateclick_ratelast_opened_atlast_clicked_atratingquery

Column to sort by. created_at orders by the contact's creation date; the rest are the per-contact engagement rollup fields (see Contact.engagement). Contacts with no rollup row always sort last. Any unrecognised value falls back to the default newest-first order.

directionstringascdescdescquery

Sort direction. Only applies when sort is set.

Response

200OKArray<Contact>

Paginated contacts

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List contacts (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/contacts'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/contacts')
data = response.json()
200
[
  {
    "id": 0,
    "brand_id": 0,
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "first_name": "string",
    "last_name": "string",
    "source": "string",
    "country_code": "string",
    "flag_emoji": "string",
    "data": {
      "tags": [
        "vip",
        "newsletter"
      ],
      "plan": "pro"
    },
    "subscribed_phone": true,
    "subscribed_email": true,
    "email": "user@example.com",
    "subscribed": {
      "email": true,
      "phone": true
    },
    "verification_status": "verified",
    "enrichment_status": "enriched",
    "mailbox_provider": "gmail",
    "list_ids": [
      0
    ],
    "last_interacted_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z",
    "engagement": {
      "rating": "engaged",
      "emails_sent": 0,
      "unique_opens": 0,
      "clicks": 0,
      "open_rate": 0,
      "click_rate": 0,
      "last_opened_at": "2024-01-15T09:30:00Z",
      "last_clicked_at": "2024-01-15T09:30:00Z"
    },
    "channels": [
      {
        "id": 0,
        "contact_id": 0,
        "kind": "email",
        "value": "string",
        "subscribed": true,
        "verified": true,
        "opt_in_at": "2024-01-15T09:30:00Z",
        "opt_out_at": "2024-01-15T09:30:00Z",
        "sent_count": 0,
        "fail_count": 0,
        "data": {},
        "created_at": "2024-01-15T09:30:00Z",
        "updated_at": "2024-01-15T09:30:00Z"
      }
    ]
  }
]

Create a contact

POST
https://api.nitrosend.com/v1/my/contacts

Body

application/json
first_namestring
last_namestring
emailstring<email>

Normalized to lowercase. If the submitted identifiers resolve cleanly to one existing contact in this brand, that contact is updated (200) instead of duplicated. If email and phone identify different contacts in this brand, the request is rejected (422). Creates/updates the email channel with opt_in.

phonestring

E.164 format. Creates/updates phone channel with opt_in

sourcestring
country_codestring
list_idsArray<integer>
dataobject

Custom key-value data. The reserved key tags holds an array of string labels used for segmentation, e.g. {"tags": ["vip", "newsletter"]}.

Merge-on-resolve behavior (create path only): when the submitted email or phone resolves cleanly to an existing contact, data is merged into the contact's existing data rather than replaced. Blank incoming values are ignored; caller-supplied non-reserved keys overwrite their counterparts; the reserved enrichment keys apollo, pdl, attio, hubspot, stripe, shopify, and nitro are preserved from the stored contact; and tags from both sides are unioned. The update endpoint (PATCH /v1/my/contacts/{id}) replaces data wholesale instead.

channels_attributesArray<object>
Show child attributes
kindstringemailphone
valuestring
subscribedboolean

Response

200OKContact

Existing contact updated in place. Returned when the submitted email and/or phone resolves cleanly to one existing contact in this brand.

201CreatedContact

Contact created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a contact
curl -X POST 'https://api.nitrosend.com/v1/my/contacts' \
  -H 'Content-Type: application/json' \
  -d '{
    "first_name": "string",
    "last_name": "string",
    "email": "user@example.com",
    "phone": "string",
    "source": "string",
    "country_code": "string",
    "list_ids": [
      0
    ],
    "data": {
      "tags": [
        "vip",
        "newsletter"
      ],
      "plan": "pro"
    },
    "channels_attributes": [
      {
        "kind": "email",
        "value": "string",
        "subscribed": true
      }
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "first_name": "string",
      "last_name": "string",
      "email": "user@example.com",
      "phone": "string",
      "source": "string",
      "country_code": "string",
      "list_ids": [
        0
      ],
      "data": {
        "tags": [
          "vip",
          "newsletter"
        ],
        "plan": "pro"
      },
      "channels_attributes": [
        {
          "kind": "email",
          "value": "string",
          "subscribed": true
        }
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "phone": "string",
  "source": "string",
  "country_code": "string",
  "list_ids": [
    0
  ],
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "channels_attributes": [
    {
      "kind": "email",
      "value": "string",
      "subscribed": True
    }
  ]
}

response = requests.post('https://api.nitrosend.com/v1/my/contacts', json=payload)
data = response.json()
Request Body
{
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "phone": "string",
  "source": "string",
  "country_code": "string",
  "list_ids": [
    0
  ],
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "channels_attributes": [
    {
      "kind": "email",
      "value": "string",
      "subscribed": true
    }
  ]
}
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a contact

GET
https://api.nitrosend.com/v1/my/contacts/{id}

Parameters

idintegerrequiredpath

Response

200OKContact

Contact with channels

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a contact
curl -X GET 'https://api.nitrosend.com/v1/my/contacts/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/contacts/{id}')
data = response.json()
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a contact

DELETE
https://api.nitrosend.com/v1/my/contacts/{id}

Parameters

idintegerrequiredpath

Response

200OKContact

Deleted contact

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a contact
curl -X DELETE 'https://api.nitrosend.com/v1/my/contacts/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/contacts/{id}')
data = response.json()
200
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

Update a contact

PATCH
https://api.nitrosend.com/v1/my/contacts/{id}

Body

application/json
first_namestring
last_namestring
emailstring<email>

Changing the email to one already owned by a different contact in the brand is rejected (422).

phonestring
sourcestring
country_codestring
list_idsArray<integer>
dataobject

Custom key-value data. The reserved key tags holds an array of string labels used for segmentation — e.g. {"tags": ["vip", "newsletter"]}.

The data object is replaced wholesale on update. Sending a partial data hash will overwrite any other keys currently stored (e.g. data.plan, enrichment metadata). Reserved enrichment namespaces include apollo, pdl, attio, hubspot, stripe, shopify, and derived nitro. To change a single key, GET the contact, merge your change into the existing data, then PATCH the full merged object back. For add/remove tag semantics across many contacts, use the nitro_manage_audience MCP tool with operation: "bulk_tag".

channels_attributesArray<object>
Show child attributes
idinteger
kindstringemailphone
valuestring
subscribedboolean
_destroyboolean

Parameters

idintegerrequiredpath

Response

200OKContact

Updated contact

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a contact
curl -X PATCH 'https://api.nitrosend.com/v1/my/contacts/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "first_name": "string",
    "last_name": "string",
    "email": "user@example.com",
    "phone": "string",
    "source": "string",
    "country_code": "string",
    "list_ids": [
      0
    ],
    "data": {
      "tags": [
        "vip",
        "newsletter"
      ],
      "plan": "pro"
    },
    "channels_attributes": [
      {
        "id": 0,
        "kind": "email",
        "value": "string",
        "subscribed": true,
        "_destroy": true
      }
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "first_name": "string",
      "last_name": "string",
      "email": "user@example.com",
      "phone": "string",
      "source": "string",
      "country_code": "string",
      "list_ids": [
        0
      ],
      "data": {
        "tags": [
          "vip",
          "newsletter"
        ],
        "plan": "pro"
      },
      "channels_attributes": [
        {
          "id": 0,
          "kind": "email",
          "value": "string",
          "subscribed": true,
          "_destroy": true
        }
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "phone": "string",
  "source": "string",
  "country_code": "string",
  "list_ids": [
    0
  ],
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "channels_attributes": [
    {
      "id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": True,
      "_destroy": True
    }
  ]
}

response = requests.patch('https://api.nitrosend.com/v1/my/contacts/{id}', json=payload)
data = response.json()
Request Body
{
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "phone": "string",
  "source": "string",
  "country_code": "string",
  "list_ids": [
    0
  ],
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "channels_attributes": [
    {
      "id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "_destroy": true
    }
  ]
}
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Re-enrich a contact via email verification + Apollo

POST
https://api.nitrosend.com/v1/my/contacts/{id}/enrich

Queues the contact for email verification and Apollo enrichment. Consumes 2 validation allowance units. Requires the contact to have an email channel.

Parameters

idintegerrequiredpath

Response

200OKContact

Enrichment queued

400Bad Request

Contact has no email or validation allowance exhausted

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Re-enrich a contact via email verification + Apollo
curl -X POST 'https://api.nitrosend.com/v1/my/contacts/{id}/enrich'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/{id}/enrich', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/contacts/{id}/enrich')
data = response.json()
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Unified activity timeline for a contact

GET
https://api.nitrosend.com/v1/my/contacts/{id}/timeline

Read-only, keyset-paginated feed merging the contact's lifecycle events and email activities into one chronological stream (newest first). Entries are normalised and whitelisted — raw rows are never exposed. Page using the cursor returned as next_cursor.

Parameters

idintegerrequiredpath
cursorstringquery

Opaque keyset cursor from a prior next_cursor.

limitinteger<= 10025query

Response

200OKobject

A page of timeline entries, newest first

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Unified activity timeline for a contact
curl -X GET 'https://api.nitrosend.com/v1/my/contacts/{id}/timeline'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/{id}/timeline', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/contacts/{id}/timeline')
data = response.json()
{
  "entries": [
    {
      "id": "string",
      "kind": "event",
      "type": "string",
      "title": "string",
      "occurred_at": "2024-01-15T09:30:00Z",
      "meta": {}
    }
  ],
  "next_cursor": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

List the field catalog for this account

GET
https://api.nitrosend.com/v1/my/contacts/fields

Returns the structured field catalog: one row per known field for this account, with key, category, type, label, promotion state, and async fill-rate. Fields are lazily registered the first time a key is seen (import, API write, or enrichment). Use PATCH /v1/my/contacts/fields/:id to update promoted or label.

Response

200OKArray<FieldCatalog>

Field catalog rows ordered by category and label

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List the field catalog for this account
curl -X GET 'https://api.nitrosend.com/v1/my/contacts/fields'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/fields', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/contacts/fields')
data = response.json()
[
  {
    "id": 0,
    "key": "string",
    "category": "contact",
    "field_type": "string",
    "label": "string",
    "promoted": true,
    "fill_rate": "75.0",
    "fill_rate_refreshed_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update a field catalog row (owner/admin only)

PATCH
https://api.nitrosend.com/v1/my/contacts/fields/{id}

Updates the promoted flag and/or the human-readable label for a field catalog row. Restricted to account owners and admins. The key, category, and field_type of a row are immutable.

Body

application/json
promotedboolean

Pin this field as a default column in the contacts grid.

labelstring

Override the human-readable label for this field.

Parameters

idintegerrequiredpath

Response

200OKFieldCatalog

Updated field catalog row

401UnauthorizedError

Not authenticated

403ForbiddenError

Not authorized

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a field catalog row (owner/admin only)
curl -X PATCH 'https://api.nitrosend.com/v1/my/contacts/fields/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "promoted": true,
    "label": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/contacts/fields/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "promoted": true,
      "label": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "promoted": True,
  "label": "string"
}

response = requests.patch('https://api.nitrosend.com/v1/my/contacts/fields/{id}', json=payload)
data = response.json()
Request Body
{
  "promoted": true,
  "label": "string"
}
{
  "id": 0,
  "key": "string",
  "category": "contact",
  "field_type": "string",
  "label": "string",
  "promoted": true,
  "fill_rate": "75.0",
  "fill_rate_refreshed_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Imports

Bulk CSV import jobs

Reserve a direct-upload blob

POST
https://api.nitrosend.com/v1/direct_uploads

Creates an Active Storage direct-upload reservation and returns a signed_id, presigned upload URL, and required upload headers. For CSV imports, send purpose: import, PUT the file bytes to direct_upload.url, then submit the returned signed_id to POST /v1/my/imports. For image media assets, send purpose: image or purpose: media_asset, PUT the image bytes to direct_upload.url, then submit the returned signed_id to POST /v1/my/images.

Body

application/json
purposestringimportimagemedia_asset

Set to import for CSV contact imports, or image/media_asset for image media assets.

blobobjectrequired
Show child attributes
filenamestringrequired
byte_sizeintegerrequired
checksumstringrequired

Base64-encoded MD5 checksum for Active Storage direct upload.

content_typestring
metadataobject

Response

200OKDirectUpload

Direct-upload reservation

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Reserve a direct-upload blob
curl -X POST 'https://api.nitrosend.com/v1/direct_uploads' \
  -H 'Content-Type: application/json' \
  -d '{
    "purpose": "import",
    "blob": {
      "filename": "contacts.csv",
      "byte_size": 1048576,
      "checksum": "string",
      "content_type": "text/csv",
      "metadata": {}
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/direct_uploads', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "purpose": "import",
      "blob": {
        "filename": "contacts.csv",
        "byte_size": 1048576,
        "checksum": "string",
        "content_type": "text/csv",
        "metadata": {}
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "purpose": "import",
  "blob": {
    "filename": "contacts.csv",
    "byte_size": 1048576,
    "checksum": "string",
    "content_type": "text/csv",
    "metadata": {}
  }
}

response = requests.post('https://api.nitrosend.com/v1/direct_uploads', json=payload)
data = response.json()
Request Body
{
  "purpose": "import",
  "blob": {
    "filename": "contacts.csv",
    "byte_size": 1048576,
    "checksum": "string",
    "content_type": "text/csv",
    "metadata": {}
  }
}
{
  "signed_id": "string",
  "filename": "string",
  "byte_size": 0,
  "content_type": "string",
  "direct_upload": {
    "url": "https://example.com",
    "headers": {}
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

List import jobs

GET
https://api.nitrosend.com/v1/my/imports

Parameters

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Import>

Paginated import jobs

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List import jobs
curl -X GET 'https://api.nitrosend.com/v1/my/imports'
const response = await fetch('https://api.nitrosend.com/v1/my/imports', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/imports')
data = response.json()
200
[
  {
    "id": 0,
    "resource": "contacts",
    "parser": "default",
    "status": "pending",
    "total_rows": 0,
    "success_rows": 0,
    "failed_rows": 0,
    "progress": {
      "status": "pending",
      "pct": 0,
      "stages": [
        {
          "key": "string",
          "label": "string",
          "count": 0,
          "state": "done"
        }
      ]
    },
    "import_errors": [
      [
        0
      ]
    ],
    "columns": {},
    "options": {},
    "assigned_list_ids": [
      0
    ],
    "assigned_lists": [
      {
        "id": 0,
        "name": "string"
      }
    ],
    "guardrail": {
      "tier": "auto",
      "status": "ok",
      "contact_us_ceiling": 250000,
      "sends_held": true
    },
    "started_at": "2024-01-15T09:30:00Z",
    "ended_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z"
  }
]

Start a CSV import

POST
https://api.nitrosend.com/v1/my/imports

Enqueues an import job from an Active Storage direct-upload signed_id. Create the blob through POST /v1/direct_uploads with purpose=import, PUT the file to the returned direct-upload URL, then submit the returned signed_id here. Poll GET /v1/my/imports/{id} for status, row counts, and row-level errors.

For contact imports, pass options as a JSON object or JSON string with list_ids to assign imported contacts to one or more lists, e.g. {"list_ids":[88]}.

Body

application/json
signed_idstringrequired

Active Storage blob signed ID returned by /v1/direct_uploads.

resourcestringcontactscontacts
parserstringdefaultdefault
dry_runbooleanfalse
columnsobject | string

CSV column mapping as a JSON object or JSON string.

optionsobject | string

Import options as a JSON object or JSON string.

Response

201CreatedImport

Import queued

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Start a CSV import
curl -X POST 'https://api.nitrosend.com/v1/my/imports' \
  -H 'Content-Type: application/json' \
  -d '{
    "signed_id": "string",
    "resource": "contacts",
    "parser": "default",
    "dry_run": false,
    "columns": {
      "email": "Email",
      "first_name": "First Name"
    },
    "options": {
      "list_ids": [
        88
      ]
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/imports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "signed_id": "string",
      "resource": "contacts",
      "parser": "default",
      "dry_run": false,
      "columns": {
        "email": "Email",
        "first_name": "First Name"
      },
      "options": {
        "list_ids": [
          88
        ]
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "signed_id": "string",
  "resource": "contacts",
  "parser": "default",
  "dry_run": False,
  "columns": {
    "email": "Email",
    "first_name": "First Name"
  },
  "options": {
    "list_ids": [
      88
    ]
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/imports', json=payload)
data = response.json()
Request Body
{
  "signed_id": "string",
  "resource": "contacts",
  "parser": "default",
  "dry_run": false,
  "columns": {
    "email": "Email",
    "first_name": "First Name"
  },
  "options": {
    "list_ids": [
      88
    ]
  }
}
{
  "id": 0,
  "resource": "contacts",
  "parser": "default",
  "status": "pending",
  "total_rows": 0,
  "success_rows": 0,
  "failed_rows": 0,
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "import_errors": [
    [
      0
    ]
  ],
  "columns": {},
  "options": {},
  "assigned_list_ids": [
    0
  ],
  "assigned_lists": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "guardrail": {
    "tier": "auto",
    "status": "ok",
    "contact_us_ceiling": 250000,
    "sends_held": true
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get import schema metadata

GET
https://api.nitrosend.com/v1/my/imports/spec

Parameters

resourcestringcontactsquery

Optional resource name. Omit to list all schemas.

Response

200OKImportSpec | object

Import schema metadata

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get import schema metadata
curl -X GET 'https://api.nitrosend.com/v1/my/imports/spec'
const response = await fetch('https://api.nitrosend.com/v1/my/imports/spec', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/imports/spec')
data = response.json()
{
  "resource": "contacts",
  "parser": "default",
  "ui": {},
  "required_rules": {},
  "fields": [
    {}
  ],
  "guardrails": {
    "max_file_size_bytes": 94371840,
    "max_file_size_mb": 90,
    "auto_max_rows": 20000,
    "contact_us_max_rows": 250000,
    "max_active_imports": 3,
    "create_rate_limit_per_minute": 10,
    "direct_upload_rate_limit_per_minute": 30
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get import job status

GET
https://api.nitrosend.com/v1/my/imports/{id}

Parameters

idintegerrequiredpath

Response

200OKImport

Import job with status, counts, and row errors

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get import job status
curl -X GET 'https://api.nitrosend.com/v1/my/imports/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/imports/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/imports/{id}')
data = response.json()
{
  "id": 0,
  "resource": "contacts",
  "parser": "default",
  "status": "pending",
  "total_rows": 0,
  "success_rows": 0,
  "failed_rows": 0,
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "import_errors": [
    [
      0
    ]
  ],
  "columns": {},
  "options": {},
  "assigned_list_ids": [
    0
  ],
  "assigned_lists": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "guardrail": {
    "tier": "auto",
    "status": "ok",
    "contact_us_ceiling": 250000,
    "sends_held": true
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete an import record

DELETE
https://api.nitrosend.com/v1/my/imports/{id}

Parameters

idintegerrequiredpath

Response

200OKImport

Deleted import

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete an import record
curl -X DELETE 'https://api.nitrosend.com/v1/my/imports/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/imports/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/imports/{id}')
data = response.json()
{
  "id": 0,
  "resource": "contacts",
  "parser": "default",
  "status": "pending",
  "total_rows": 0,
  "success_rows": 0,
  "failed_rows": 0,
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "import_errors": [
    [
      0
    ]
  ],
  "columns": {},
  "options": {},
  "assigned_list_ids": [
    0
  ],
  "assigned_lists": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "guardrail": {
    "tier": "auto",
    "status": "ok",
    "contact_us_ceiling": 250000,
    "sends_held": true
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Cancel a pending or processing import

POST
https://api.nitrosend.com/v1/my/imports/{id}/cancel

Parameters

idintegerrequiredpath

Response

200OKImport

Canceled import

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Cancel a pending or processing import
curl -X POST 'https://api.nitrosend.com/v1/my/imports/{id}/cancel'
const response = await fetch('https://api.nitrosend.com/v1/my/imports/{id}/cancel', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/imports/{id}/cancel')
data = response.json()
{
  "id": 0,
  "resource": "contacts",
  "parser": "default",
  "status": "pending",
  "total_rows": 0,
  "success_rows": 0,
  "failed_rows": 0,
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "import_errors": [
    [
      0
    ]
  ],
  "columns": {},
  "options": {},
  "assigned_list_ids": [
    0
  ],
  "assigned_lists": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "guardrail": {
    "tier": "auto",
    "status": "ok",
    "contact_us_ceiling": 250000,
    "sends_held": true
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Exports

Contact CSV export jobs

List export jobs

GET
https://api.nitrosend.com/v1/my/exports

Parameters

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Export>

Paginated export jobs

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List export jobs
curl -X GET 'https://api.nitrosend.com/v1/my/exports'
const response = await fetch('https://api.nitrosend.com/v1/my/exports', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/exports')
data = response.json()
200
[
  {
    "id": 0,
    "resource": "contacts",
    "format": "csv",
    "status": "pending",
    "total_rows": 0,
    "rows_written": 0,
    "error_message": "string",
    "ready": true,
    "download_path": "string",
    "progress": {
      "status": "pending",
      "pct": 0,
      "stages": [
        {
          "key": "string",
          "label": "string",
          "count": 0,
          "state": "done"
        }
      ]
    },
    "started_at": "2024-01-15T09:30:00Z",
    "ended_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z"
  }
]

Start a contact export

POST
https://api.nitrosend.com/v1/my/exports

Enqueues an export job that builds a CSV of the brand's contacts and attaches it to the export record. The same filters as GET /v1/my/contacts are supported (search, list_id, tag). Custom fields stored on contacts are emitted as additional CSV columns.

Poll GET /v1/my/exports/{id} until ready is true, then download the file from the returned download_path.

Body

application/json
resourcestringcontactscontacts
searchstring

Free-text filter, matching the contacts list search.

list_idinteger

Restrict the export to contacts in this list.

tagstring

Restrict the export to contacts with this tag.

Response

201CreatedExport

Export queued

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Start a contact export
curl -X POST 'https://api.nitrosend.com/v1/my/exports' \
  -H 'Content-Type: application/json' \
  -d '{
    "resource": "contacts",
    "search": "string",
    "list_id": 0,
    "tag": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/exports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "resource": "contacts",
      "search": "string",
      "list_id": 0,
      "tag": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "resource": "contacts",
  "search": "string",
  "list_id": 0,
  "tag": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/exports', json=payload)
data = response.json()
Request Body
{
  "resource": "contacts",
  "search": "string",
  "list_id": 0,
  "tag": "string"
}
{
  "id": 0,
  "resource": "contacts",
  "format": "csv",
  "status": "pending",
  "total_rows": 0,
  "rows_written": 0,
  "error_message": "string",
  "ready": true,
  "download_path": "string",
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get an export job

GET
https://api.nitrosend.com/v1/my/exports/{id}

Parameters

idintegerrequiredpath

Response

200OKExport

Export job

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get an export job
curl -X GET 'https://api.nitrosend.com/v1/my/exports/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/exports/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/exports/{id}')
data = response.json()
{
  "id": 0,
  "resource": "contacts",
  "format": "csv",
  "status": "pending",
  "total_rows": 0,
  "rows_written": 0,
  "error_message": "string",
  "ready": true,
  "download_path": "string",
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Download a completed export file

GET
https://api.nitrosend.com/v1/my/exports/{id}/download

Returns the CSV file once the export status is complete.

Parameters

idintegerrequiredpath

Response

200OKstring<binary>

CSV file

404Not FoundError

Resource not found

422Unprocessable EntityError

Export is not ready

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Download a completed export file
curl -X GET 'https://api.nitrosend.com/v1/my/exports/{id}/download'
const response = await fetch('https://api.nitrosend.com/v1/my/exports/{id}/download', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/exports/{id}/download')
data = response.json()
"<binary>"
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Lists

Contact lists

List contact lists (paginated)

GET
https://api.nitrosend.com/v1/my/lists

Parameters

namestringquery

Exact case-insensitive list name filter

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<ContactList>

Paginated contact lists

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List contact lists (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/lists'
const response = await fetch('https://api.nitrosend.com/v1/my/lists', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/lists')
data = response.json()
200
[
  {
    "id": 0,
    "account_id": 0,
    "brand_id": 0,
    "name": "string",
    "contacts_count": 0,
    "segment_id": 0,
    "stale": true,
    "last_populated_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Create a contact list

POST
https://api.nitrosend.com/v1/my/lists

Body

application/json
namestringrequired
segment_idinteger | null
contact_idsArray<integer>

Response

201CreatedContactList

List created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a contact list
curl -X POST 'https://api.nitrosend.com/v1/my/lists' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "segment_id": 0,
    "contact_ids": [
      0
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/lists', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "segment_id": 0,
      "contact_ids": [
        0
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "segment_id": 0,
  "contact_ids": [
    0
  ]
}

response = requests.post('https://api.nitrosend.com/v1/my/lists', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "segment_id": 0,
  "contact_ids": [
    0
  ]
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "contacts_count": 0,
  "segment_id": 0,
  "stale": true,
  "last_populated_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a contact list

GET
https://api.nitrosend.com/v1/my/lists/{id}

Parameters

idintegerrequiredpath

Response

200OKContactList

Contact list

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a contact list
curl -X GET 'https://api.nitrosend.com/v1/my/lists/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/lists/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/lists/{id}')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "contacts_count": 0,
  "segment_id": 0,
  "stale": true,
  "last_populated_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a contact list

DELETE
https://api.nitrosend.com/v1/my/lists/{id}

Parameters

idintegerrequiredpath

Response

200OKContactList

Deleted list

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a contact list
curl -X DELETE 'https://api.nitrosend.com/v1/my/lists/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/lists/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/lists/{id}')
data = response.json()
200
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "contacts_count": 0,
  "segment_id": 0,
  "stale": true,
  "last_populated_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Update a contact list

PATCH
https://api.nitrosend.com/v1/my/lists/{id}

Body

application/json
namestring
segment_idinteger | null
contact_idsArray<integer>

Parameters

idintegerrequiredpath

Response

200OKContactList

Updated list

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a contact list
curl -X PATCH 'https://api.nitrosend.com/v1/my/lists/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "segment_id": 0,
    "contact_ids": [
      0
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/lists/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "segment_id": 0,
      "contact_ids": [
        0
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "segment_id": 0,
  "contact_ids": [
    0
  ]
}

response = requests.patch('https://api.nitrosend.com/v1/my/lists/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "segment_id": 0,
  "contact_ids": [
    0
  ]
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "contacts_count": 0,
  "segment_id": 0,
  "stale": true,
  "last_populated_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get delete warning metadata for a contact list

GET
https://api.nitrosend.com/v1/my/lists/{id}/delete_warning

Parameters

idintegerrequiredpath

Response

200OKListDeleteWarning

Flows connected to this list

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get delete warning metadata for a contact list
curl -X GET 'https://api.nitrosend.com/v1/my/lists/{id}/delete_warning'
const response = await fetch('https://api.nitrosend.com/v1/my/lists/{id}/delete_warning', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/lists/{id}/delete_warning')
data = response.json()
200
{
  "campaign_names": [
    "string"
  ],
  "flow_names": [
    "string"
  ]
}

Add or remove existing contacts from a list by email

POST
https://api.nitrosend.com/v1/my/lists/{id}/contacts/bulk

Public REST equivalent of the list membership batch primitive. The endpoint resolves existing contacts by email within the current brand and adds or removes memberships idempotently. It does not create contacts; emails with no current-brand contact are returned in not_found.

Body

application/json
actionstringaddremoverequired

Add existing contacts to the list or remove them from it.

emailsArray<string>required

Parameters

idintegerrequiredpath

Response

200OKBulkListContactsResponse

Bulk list membership result

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Add or remove existing contacts from a list by email
curl -X POST 'https://api.nitrosend.com/v1/my/lists/{id}/contacts/bulk' \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "add",
    "emails": [
      "user@example.com"
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/lists/{id}/contacts/bulk', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "action": "add",
      "emails": [
        "user@example.com"
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "action": "add",
  "emails": [
    "user@example.com"
  ]
}

response = requests.post('https://api.nitrosend.com/v1/my/lists/{id}/contacts/bulk', json=payload)
data = response.json()
Request Body
{
  "action": "add",
  "emails": [
    "user@example.com"
  ]
}
{
  "action": "add",
  "list_id": 0,
  "added": 0,
  "removed": 0,
  "already_in_list": [
    "user@example.com"
  ],
  "not_in_list": [
    "user@example.com"
  ],
  "not_found": [
    "user@example.com"
  ],
  "invalid_emails": [
    "string"
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Segments

Dynamic contact segments

List all segments (paginated)

GET
https://api.nitrosend.com/v1/my/segments

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<Segment>

Segments

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List all segments (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/segments'
const response = await fetch('https://api.nitrosend.com/v1/my/segments', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/segments')
data = response.json()
200
[]

Create a segment

POST
https://api.nitrosend.com/v1/my/segments

Body

application/json
namestringrequired
filtersSegmentFilterExpression

Response

201CreatedSegment

Segment created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a segment
curl -X POST 'https://api.nitrosend.com/v1/my/segments' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/segments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/segments', json=payload)
data = response.json()
Request Body
{
  "name": "string"
}
422
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a segment

GET
https://api.nitrosend.com/v1/my/segments/{id}

Parameters

idintegerrequiredpath

Response

200OKSegment

Segment

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a segment
curl -X GET 'https://api.nitrosend.com/v1/my/segments/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/segments/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/segments/{id}')
data = response.json()
404
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a segment

DELETE
https://api.nitrosend.com/v1/my/segments/{id}

Parameters

idintegerrequiredpath

Response

200OKSegment

Deleted segment

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a segment
curl -X DELETE 'https://api.nitrosend.com/v1/my/segments/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/segments/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/segments/{id}')
data = response.json()

Update a segment

PATCH
https://api.nitrosend.com/v1/my/segments/{id}

Body

application/json
namestring
filtersSegmentFilterExpression

Parameters

idintegerrequiredpath

Response

200OKSegment

Updated segment

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a segment
curl -X PATCH 'https://api.nitrosend.com/v1/my/segments/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/segments/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string"
}

response = requests.patch('https://api.nitrosend.com/v1/my/segments/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string"
}
422
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Count contacts matching filters

POST
https://api.nitrosend.com/v1/my/segments/count

Preview how many contacts match a set of segment filters without creating or saving a segment.

Body

application/json
filtersSegmentFilterExpression

Response

200OKobject

Contact count

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Count contacts matching filters
curl -X POST 'https://api.nitrosend.com/v1/my/segments/count' \
  -H 'Content-Type: application/json' \
  -d '{}'
const response = await fetch('https://api.nitrosend.com/v1/my/segments/count', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({}),
});

const data = await response.json();
import requests

payload = {}

response = requests.post('https://api.nitrosend.com/v1/my/segments/count', json=payload)
data = response.json()
Request Body
{}
{
  "count": 0
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Preview contacts matching filters

POST
https://api.nitrosend.com/v1/my/segments/preview

Preview a prospective segment without saving it. Returns a live count, a bounded contact sample, and bounded overlap with existing segments. Invalid filters fail closed with invalid_filter.

Body

application/json
filtersSegmentFilterExpression

Response

200OKSegmentPreview

Segment preview

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Preview contacts matching filters
curl -X POST 'https://api.nitrosend.com/v1/my/segments/preview' \
  -H 'Content-Type: application/json' \
  -d '{}'
const response = await fetch('https://api.nitrosend.com/v1/my/segments/preview', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({}),
});

const data = await response.json();
import requests

payload = {}

response = requests.post('https://api.nitrosend.com/v1/my/segments/preview', json=payload)
data = response.json()
Request Body
{}
{
  "count": 0,
  "sample": [
    {
      "id": 0,
      "email": "string",
      "name": "string"
    }
  ],
  "overlap": [
    {
      "segment_id": 0,
      "name": "string",
      "overlap_count": 0
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Campaigns

Email and SMS campaigns

List campaigns (paginated)

GET
https://api.nitrosend.com/v1/my/campaigns

Parameters

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Campaign>

Paginated campaigns

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List campaigns (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/campaigns'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/campaigns')
data = response.json()
200
[
  {
    "id": 0,
    "account_id": 0,
    "brand_id": 0,
    "status": "draft",
    "approval_state": "string",
    "channel": "email",
    "name": "string",
    "data": {},
    "scheduled_at": "2024-01-15T09:30:00Z",
    "sent_count": 0,
    "dashboard_url": "https://example.com",
    "preview_url": "https://example.com",
    "recipient_snapshot": {
      "requested_recipients": 0,
      "dispatched_recipients": 0,
      "blocked_recipients": 0,
      "requested_send_units": 0,
      "dispatched_send_units": 0,
      "units_per_recipient": 0,
      "send_token": "string",
      "started_at": "2024-01-15T09:30:00Z",
      "completed_at": "2024-01-15T09:30:00Z"
    },
    "last_send_recipients": 0,
    "delivery": {
      "campaign_send_token": "string",
      "status": "sending",
      "recipients": 0,
      "sent": 0,
      "failed": 0,
      "pending": 0
    },
    "engagement": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0,
      "account": {
        "sent": 0,
        "opens": 0,
        "total_opens": 0,
        "open_rate": 0
      }
    },
    "editable": true,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z",
    "trigger": {
      "id": 0,
      "flow_id": 0,
      "event": "string",
      "audience_type": "lists",
      "segment_id": 0,
      "contact_list_id": 0,
      "contact_list_ids": [
        0
      ],
      "exclude_segment_ids": [
        0
      ],
      "exclude_contact_list_ids": [
        0
      ],
      "data": {},
      "triggered_count": 0,
      "last_triggered_at": "2024-01-15T09:30:00Z",
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    },
    "template": {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    },
    "templates": [
      {
        "id": 0,
        "name": "string",
        "flow_id": 0,
        "action_id": 0,
        "version": 0,
        "subject": "string",
        "body": "string",
        "preheader": "string",
        "from_name": "string",
        "from_email": "string",
        "reply_to": "string",
        "design": {
          "sections": [
            {
              "type": "header",
              "props": {},
              "styles": {
                "background_color": "string",
                "padding": "string",
                "align": "left",
                "font_size": 0,
                "text_color": "string",
                "border_radius": 0
              }
            }
          ],
          "theme": {
            "brand_color": "string",
            "bg_color": "string",
            "text_color": "string",
            "font_body": "string",
            "font_heading": "string",
            "logo_url": "string"
          }
        },
        "variables": {},
        "generation_id": 0,
        "created_at": "2024-01-15T09:30:00Z",
        "updated_at": "2024-01-15T09:30:00Z"
      }
    ]
  }
]

Create a campaign

POST
https://api.nitrosend.com/v1/my/campaigns

Body

application/json
namestringrequired
channelstringemailsmsemail

Response

201CreatedCampaign

Campaign created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a campaign
curl -X POST 'https://api.nitrosend.com/v1/my/campaigns' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "channel": "email"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "channel": "email"
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "channel": "email"
}

response = requests.post('https://api.nitrosend.com/v1/my/campaigns', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "channel": "email"
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a campaign

GET
https://api.nitrosend.com/v1/my/campaigns/{id}

Parameters

idintegerrequiredpath

Response

200OKCampaign

Campaign with trigger, template, and templates

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a campaign
curl -X GET 'https://api.nitrosend.com/v1/my/campaigns/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/campaigns/{id}')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a campaign

DELETE
https://api.nitrosend.com/v1/my/campaigns/{id}

Parameters

idintegerrequiredpath

Response

200OKCampaign

Deleted campaign

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a campaign
curl -X DELETE 'https://api.nitrosend.com/v1/my/campaigns/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/campaigns/{id}')
data = response.json()
200
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

Update a campaign

PATCH
https://api.nitrosend.com/v1/my/campaigns/{id}

Updates the campaign. When the campaign is no longer editable (see the editable field on the Campaign schema — false for live/paused/ completed/cancelled/archived, and for scheduled campaigns within 5 minutes of their send time), only name-only payloads and status transitions (Resume, Cancel) are accepted. Any other attribute returns 422 with error_code: "campaign_locked". Use the /duplicate endpoint to fork a sent campaign into a new draft.

Body

application/json
namestring
statusstring
channelstringemailsms
scheduled_atstring<date-time> | null
trigger_attributesobject
Show child attributes
eventstring
audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target. Use all_contacts only for deliberate all-subscribed-contact sends.

contact_list_idinteger | nulldeprecated
contact_list_idsArray<integer>
segment_idinteger | null
exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded; pass [] to clear

exclude_contact_list_idsArray<integer>

Contact list IDs whose members are excluded; pass [] to clear

dataobject
template_attributesobject
Show child attributes
if_versionintegerrequired

Required optimistic concurrency token for template content writes. Use the current template.version. A stale value returns 409 with current_version and expected_version.

subjectstring
bodystring
preheaderstring
from_namestring
from_emailstring<email>
reply_tostring<email>
designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring

Parameters

idintegerrequiredpath

Response

200OKCampaign

Updated campaign

409ConflictError

Template version conflict

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a campaign
curl -X PATCH 'https://api.nitrosend.com/v1/my/campaigns/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "status": "string",
    "channel": "email",
    "scheduled_at": "2024-01-15T09:30:00Z",
    "trigger_attributes": {
      "event": "string",
      "audience_type": "lists",
      "contact_list_id": 0,
      "contact_list_ids": [
        0
      ],
      "segment_id": 0,
      "exclude_segment_ids": [
        0
      ],
      "exclude_contact_list_ids": [
        0
      ],
      "data": {}
    },
    "template_attributes": {
      "if_version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "user@example.com",
      "reply_to": "user@example.com",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "status": "string",
      "channel": "email",
      "scheduled_at": "2024-01-15T09:30:00Z",
      "trigger_attributes": {
        "event": "string",
        "audience_type": "lists",
        "contact_list_id": 0,
        "contact_list_ids": [
          0
        ],
        "segment_id": 0,
        "exclude_segment_ids": [
          0
        ],
        "exclude_contact_list_ids": [
          0
        ],
        "data": {}
      },
      "template_attributes": {
        "if_version": 0,
        "subject": "string",
        "body": "string",
        "preheader": "string",
        "from_name": "string",
        "from_email": "user@example.com",
        "reply_to": "user@example.com",
        "design": {
          "sections": [
            {
              "type": "header",
              "props": {},
              "styles": {
                "background_color": "string",
                "padding": "string",
                "align": "left",
                "font_size": 0,
                "text_color": "string",
                "border_radius": 0
              }
            }
          ],
          "theme": {
            "brand_color": "string",
            "bg_color": "string",
            "text_color": "string",
            "font_body": "string",
            "font_heading": "string",
            "logo_url": "string"
          }
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "status": "string",
  "channel": "email",
  "scheduled_at": "2024-01-15T09:30:00Z",
  "trigger_attributes": {
    "event": "string",
    "audience_type": "lists",
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "segment_id": 0,
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {}
  },
  "template_attributes": {
    "if_version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "user@example.com",
    "reply_to": "user@example.com",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }
}

response = requests.patch('https://api.nitrosend.com/v1/my/campaigns/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "status": "string",
  "channel": "email",
  "scheduled_at": "2024-01-15T09:30:00Z",
  "trigger_attributes": {
    "event": "string",
    "audience_type": "lists",
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "segment_id": 0,
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {}
  },
  "template_attributes": {
    "if_version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "user@example.com",
    "reply_to": "user@example.com",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Duplicate a campaign

POST
https://api.nitrosend.com/v1/my/campaigns/{id}/duplicate

Creates a new draft campaign that copies the source's audience (trigger.audience_type, contact_list_ids, segment_id, exclude_segment_ids, exclude_contact_list_ids) and template content (design, subject, preheader, body, from_name, from_email, reply_to). Resets status to draft, approval_state to pending_review, scheduled_at to null, and trigger.event to manual. Works on any status — this is how clients fork a sent campaign into a new draft. Sessions, activities, approvals, and metrics are not copied.

Pass cancel_source: true to atomically cancel the source campaign in the same transaction as the duplicate — useful for 'cancel and duplicate' flows on paused campaigns.

Body

application/json
cancel_sourcebooleanfalse

When true, cancels the source campaign in the same DB transaction as the duplicate creation.

Parameters

idintegerrequiredpath

Response

201CreatedCampaign

New draft campaign

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Duplicate a campaign
curl -X POST 'https://api.nitrosend.com/v1/my/campaigns/{id}/duplicate' \
  -H 'Content-Type: application/json' \
  -d '{
    "cancel_source": false
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}/duplicate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "cancel_source": false
    }),
});

const data = await response.json();
import requests

payload = {
  "cancel_source": False
}

response = requests.post('https://api.nitrosend.com/v1/my/campaigns/{id}/duplicate', json=payload)
data = response.json()
Request Body
{
  "cancel_source": false
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Render a campaign email to HTML

GET
https://api.nitrosend.com/v1/my/campaigns/{id}/render

Renders the campaign's current template through the shared preview renderer.

Parameters

idintegerrequiredpath

Response

200OKobject

Rendered HTML

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Render a campaign email to HTML
curl -X GET 'https://api.nitrosend.com/v1/my/campaigns/{id}/render'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}/render', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/campaigns/{id}/render')
data = response.json()
{
  "html": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get current campaign delivery progress

GET
https://api.nitrosend.com/v1/my/campaigns/{id}/delivery

Polls the current campaign send using the same Campaign::SendProgress payload exposed on the campaign serializer. Active sends return status: "sending" with poll_after_seconds; terminal sends return status: "completed" or status: "paused" when a deliverability guard stopped the active send. Campaigns that have not started delivery return status: "not_started". Polling also refreshes server-side progress: queued reservations older than 15 minutes are marked failed before the response is returned, so failed/pending can change on a poll even when no provider webhook has arrived.

Parameters

idintegerrequiredpath

Response

200OKCampaignDeliveryProgress

Current campaign delivery progress

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get current campaign delivery progress
curl -X GET 'https://api.nitrosend.com/v1/my/campaigns/{id}/delivery'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}/delivery', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/campaigns/{id}/delivery')
data = response.json()
{
  "campaign_send_token": "string",
  "status": "not_started",
  "recipients": 0,
  "sent": 0,
  "failed": 0,
  "pending": 0,
  "terminal": true,
  "poll_after_seconds": 0
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Send a test email for a campaign

POST
https://api.nitrosend.com/v1/my/campaigns/{id}/send_test

Thin adapter over the same test-send service used by template tests.

Body

application/json
emailstring<email>
emailsArray<string>
send_test_toArray<string>
contact_idinteger

Parameters

idintegerrequiredpath

Response

200OKobject

Test email sent

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Send a test email for a campaign
curl -X POST 'https://api.nitrosend.com/v1/my/campaigns/{id}/send_test' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "user@example.com",
    "emails": [
      "user@example.com"
    ],
    "send_test_to": [
      "user@example.com"
    ],
    "contact_id": 0
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}/send_test', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "email": "user@example.com",
      "emails": [
        "user@example.com"
      ],
      "send_test_to": [
        "user@example.com"
      ],
      "contact_id": 0
    }),
});

const data = await response.json();
import requests

payload = {
  "email": "user@example.com",
  "emails": [
    "user@example.com"
  ],
  "send_test_to": [
    "user@example.com"
  ],
  "contact_id": 0
}

response = requests.post('https://api.nitrosend.com/v1/my/campaigns/{id}/send_test', json=payload)
data = response.json()
Request Body
{
  "email": "user@example.com",
  "emails": [
    "user@example.com"
  ],
  "send_test_to": [
    "user@example.com"
  ],
  "contact_id": 0
}
{
  "sent": 0,
  "results": [
    {
      "email": "string",
      "success": true
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Send a campaign

POST
https://api.nitrosend.com/v1/my/campaigns/{id}/send

Transitions the campaign and its flow to live, then delivers. Optionally update trigger and template attributes in the same request. To schedule instead of sending now, pass the delivery time as trigger_attributes.data.deliver_at (RFC 3339); omit it to send immediately. A top-level scheduled_at (or a nested trigger_attributes.data.scheduled_at) is not accepted here and returns 422 scheduled_at_not_accepted rather than being silently ignored — scheduled_at is the create/update field, not the send field. Returns 422 campaign_locked if the campaign is not editable (live, paused, completed, cancelled, archived, or scheduled within 5 minutes of send). Returns 409 duplicate_campaign_schedule or duplicate_campaign_send when a repeat attempt would create another schedule or overlap an active send; poll /delivery, cancel, or edit the existing scheduled campaign instead.

Body

application/json
confirm_send_to_allboolean

Required when trigger_attributes.audience_type or the saved trigger audience_type is all_contacts.

trigger_attributesobject
Show child attributes
eventstring
audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target. Use all_contacts only for deliberate all-subscribed-contact sends.

contact_list_idinteger | nulldeprecated
contact_list_idsArray<integer>
segment_idinteger | null
exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded; pass [] to clear

exclude_contact_list_idsArray<integer>

Contact list IDs whose members are excluded; pass [] to clear

dataobject
template_attributesobject
Show child attributes
if_versionintegerrequired

Required optimistic concurrency token for template content writes. Use the current template.version. A stale value returns 409 with current_version and expected_version.

subjectstring
bodystring
preheaderstring
from_namestring
from_emailstring<email>
reply_tostring<email>
designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring

Parameters

idintegerrequiredpath

Response

200OKCampaign

Campaign sent

409ConflictError

Template version conflict or duplicate send attempt

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Send a campaign
curl -X POST 'https://api.nitrosend.com/v1/my/campaigns/{id}/send' \
  -H 'Content-Type: application/json' \
  -d '{
    "confirm_send_to_all": true,
    "trigger_attributes": {
      "event": "string",
      "audience_type": "lists",
      "contact_list_id": 0,
      "contact_list_ids": [
        0
      ],
      "segment_id": 0,
      "exclude_segment_ids": [
        0
      ],
      "exclude_contact_list_ids": [
        0
      ],
      "data": {}
    },
    "template_attributes": {
      "if_version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "user@example.com",
      "reply_to": "user@example.com",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/campaigns/{id}/send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "confirm_send_to_all": true,
      "trigger_attributes": {
        "event": "string",
        "audience_type": "lists",
        "contact_list_id": 0,
        "contact_list_ids": [
          0
        ],
        "segment_id": 0,
        "exclude_segment_ids": [
          0
        ],
        "exclude_contact_list_ids": [
          0
        ],
        "data": {}
      },
      "template_attributes": {
        "if_version": 0,
        "subject": "string",
        "body": "string",
        "preheader": "string",
        "from_name": "string",
        "from_email": "user@example.com",
        "reply_to": "user@example.com",
        "design": {
          "sections": [
            {
              "type": "header",
              "props": {},
              "styles": {
                "background_color": "string",
                "padding": "string",
                "align": "left",
                "font_size": 0,
                "text_color": "string",
                "border_radius": 0
              }
            }
          ],
          "theme": {
            "brand_color": "string",
            "bg_color": "string",
            "text_color": "string",
            "font_body": "string",
            "font_heading": "string",
            "logo_url": "string"
          }
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "confirm_send_to_all": True,
  "trigger_attributes": {
    "event": "string",
    "audience_type": "lists",
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "segment_id": 0,
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {}
  },
  "template_attributes": {
    "if_version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "user@example.com",
    "reply_to": "user@example.com",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/campaigns/{id}/send', json=payload)
data = response.json()
Request Body
{
  "confirm_send_to_all": true,
  "trigger_attributes": {
    "event": "string",
    "audience_type": "lists",
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "segment_id": 0,
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {}
  },
  "template_attributes": {
    "if_version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "user@example.com",
    "reply_to": "user@example.com",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }
}
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Messages

Transactional email and SMS messages

List messages

GET
https://api.nitrosend.com/v1/my/messages

Returns all messages by default. Use source_type to narrow to campaign, flow, transactional, or test sends.

Parameters

source_typestringallcampaignflowtransactionaltestquery

Filter by source. Omit or use 'all' for everything.

flow_idintegerquery

Filter to messages from a specific flow

campaign_idintegerquery

Filter to messages from a specific campaign

datestring<date>query

Filter to messages created on this date

channelstringemailsmsquery
statusstringqueuedsentfailedquery
pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Message>

Paginated list of messages

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List messages
curl -X GET 'https://api.nitrosend.com/v1/my/messages'
const response = await fetch('https://api.nitrosend.com/v1/my/messages', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/messages')
data = response.json()
200
[
  {
    "id": 0,
    "channel": "email",
    "to": "string",
    "subject": "string",
    "status": "queued",
    "provider_id": "string",
    "flow_id": 0,
    "source_type": "campaign",
    "source_name": "string",
    "sent_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z"
  }
]

Send a transactional message

POST
https://api.nitrosend.com/v1/my/messages

Send a transactional email or SMS to a single recipient immediately. No campaign, no audience, no approval required. Use for receipts, password resets, OTPs, order confirmations, and system notifications.

Body

application/json
channelstringemailsmsrequired
tostringrequired

Recipient email address or E.164 phone number

subjectstring

Email subject line (required for email channel)

bodystring

Message body. Required for SMS. For email this is plain text (HTML in this field is escaped) and serves as the plain-text alternative when html is set. To send HTML, use html or template_id.

htmlstring

Pre-rendered HTML body for email, used verbatim as the HTML part (not escaped). Use this to send your own fully-rendered HTML. Mutually exclusive with template_id (email only).

template_idinteger

Load email design from an existing template (email only)

contact_idinteger

Optional contact to personalize with when rendering a template

fromstring

Verified sender email for this email message. May include a display name, for example "Acme hello@example.com".

from_emailstring

Verified sender email for this email message. Alias of from.

from_namestring

Sender display name for this email message

reply_tostring

Reply-to email address for this email message

headersobject

Provider headers for this email message. Structural and Nitrosend-reserved headers are rejected.

tagsobject

Provider tags for this email message. Nitrosend-reserved tag keys are rejected and system tags are always controlled by Nitrosend.

dataobject

Merge variables

idempotency_keystring

Idempotency key (alternative to header)

Parameters

Idempotency-Keystringheader

Prevents duplicate sends on retry. The same key with the same payload returns the original message; the same key with a different payload returns 409.

Response

200OKMessage

Existing message returned for idempotency replay

201CreatedMessage

Message created

409Conflictobject

Idempotency key was already used with a different payload

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Send a transactional message
curl -X POST 'https://api.nitrosend.com/v1/my/messages' \
  -H 'Content-Type: application/json' \
  -d '{
    "channel": "email",
    "to": "string",
    "subject": "string",
    "body": "string",
    "html": "string",
    "template_id": 0,
    "contact_id": 0,
    "from": "string",
    "from_email": "string",
    "from_name": "string",
    "reply_to": "string",
    "headers": {},
    "tags": {},
    "data": {},
    "idempotency_key": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "channel": "email",
      "to": "string",
      "subject": "string",
      "body": "string",
      "html": "string",
      "template_id": 0,
      "contact_id": 0,
      "from": "string",
      "from_email": "string",
      "from_name": "string",
      "reply_to": "string",
      "headers": {},
      "tags": {},
      "data": {},
      "idempotency_key": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "channel": "email",
  "to": "string",
  "subject": "string",
  "body": "string",
  "html": "string",
  "template_id": 0,
  "contact_id": 0,
  "from": "string",
  "from_email": "string",
  "from_name": "string",
  "reply_to": "string",
  "headers": {},
  "tags": {},
  "data": {},
  "idempotency_key": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/messages', json=payload)
data = response.json()
Request Body
{
  "channel": "email",
  "to": "string",
  "subject": "string",
  "body": "string",
  "html": "string",
  "template_id": 0,
  "contact_id": 0,
  "from": "string",
  "from_email": "string",
  "from_name": "string",
  "reply_to": "string",
  "headers": {},
  "tags": {},
  "data": {},
  "idempotency_key": "string"
}
{
  "id": 0,
  "channel": "email",
  "to": "string",
  "subject": "string",
  "status": "queued",
  "provider_id": "string",
  "flow_id": 0,
  "source_type": "campaign",
  "source_name": "string",
  "sent_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "id": 0,
  "channel": "email",
  "to": "string",
  "subject": "string",
  "status": "queued",
  "provider_id": "string",
  "flow_id": 0,
  "source_type": "campaign",
  "source_name": "string",
  "sent_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 409,
  "message": "string",
  "error": true,
  "error_code": "idempotency_conflict"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a transactional message

GET
https://api.nitrosend.com/v1/my/messages/{id}

Parameters

idintegerrequiredpath

Response

200OKMessage

Message

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a transactional message
curl -X GET 'https://api.nitrosend.com/v1/my/messages/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/messages/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/messages/{id}')
data = response.json()
{
  "id": 0,
  "channel": "email",
  "to": "string",
  "subject": "string",
  "status": "queued",
  "provider_id": "string",
  "flow_id": 0,
  "source_type": "campaign",
  "source_name": "string",
  "sent_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Render a transactional message's HTML for preview

GET
https://api.nitrosend.com/v1/my/messages/{id}/preview

Returns the as-composed HTML for a message — from its template (rendered design), raw html, or the escaped plain-text body — using the same renderer as the send path, for display in a sandboxed iframe. Non-email or unrenderable messages (deleted/design-less template) return a typed empty state (empty: true) rather than an error.

Parameters

idintegerrequiredpath

Response

200OKobject

Rendered preview, or a typed empty state.

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Render a transactional message's HTML for preview
curl -X GET 'https://api.nitrosend.com/v1/my/messages/{id}/preview'
const response = await fetch('https://api.nitrosend.com/v1/my/messages/{id}/preview', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/messages/{id}/preview')
data = response.json()
{
  "html": "string",
  "format": "template",
  "empty": true,
  "reason": "not_previewable"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Suppressions

Account suppression list and deliverability diagnostics

List suppressions

GET
https://api.nitrosend.com/v1/my/suppressions

Returns the authenticated account's current suppression records by default, including bounded provider diagnostics when the source feedback event is available.

Parameters

idintegerquery

Filter to a specific suppression ID

emailstring<email>query

Filter to a specific suppressed email address

reasonstringhard_bouncesoft_bouncecomplaintmanualadminquery
source_providerstringquery

Filter by provider that emitted the source event

activebooleantruequery

Defaults to true. Set false to list expired suppressions.

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Suppression>

Paginated list of suppressions

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List suppressions
curl -X GET 'https://api.nitrosend.com/v1/my/suppressions'
const response = await fetch('https://api.nitrosend.com/v1/my/suppressions', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/suppressions')
data = response.json()
200
[
  {
    "id": 0,
    "email": "user@example.com",
    "reason": "hard_bounce",
    "scope": "account_scoped",
    "active": true,
    "contact_id": 0,
    "source_provider": "string",
    "source_event_id": "string",
    "provider_diagnostic": "string",
    "bounce_type": "hard",
    "bounce_subtype": "string",
    "complaint_feedback_type": "string",
    "event_occurred_at": "2024-01-15T09:30:00Z",
    "expires_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Templates

Email templates, preview, and component schema

Ingest an image media asset

POST
https://api.nitrosend.com/v1/my/images

REST adapter over the same image ingest capability used by MCP. Send exactly one source: image_data, image_url, multipart file, or a direct-upload signed_id created with purpose image or media_asset. The v1 media contract supports media_kind: image only.

Body

application/json
application/jsonobject
image_datastring

Raw base64 image bytes or a data URL. PNG, JPEG, or WebP only; decoded size must be under 10MB.

image_urlstring<uri>

Public PNG, JPEG, or WebP URL to ingest when Nitro-hosted permanence is desired.

signed_idstring

Active Storage blob signed ID returned by /v1/direct_uploads after uploading bytes with purpose image or media_asset.

filenamestring

Original filename for image_data uploads, or optional filename override for image_url/signed_id sources.

content_typestring | null

Optional MIME type hint when image_data is raw base64 rather than a data URL.

multipart/form-dataobject
filestring<binary>

PNG, JPEG, or WebP file upload. Must be under 10MB.

filenamestring

Optional filename override.

content_typestring | null

Optional MIME type override.

Response

201CreatedImageAsset

Ingested image asset

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Ingest an image media asset
curl -X POST 'https://api.nitrosend.com/v1/my/images' \
  -H 'Content-Type: application/json' \
  -d '{
    "image_data": "string",
    "image_url": "https://example.com",
    "signed_id": "string",
    "filename": "string",
    "content_type": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/images', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "image_data": "string",
      "image_url": "https://example.com",
      "signed_id": "string",
      "filename": "string",
      "content_type": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "image_data": "string",
  "image_url": "https://example.com",
  "signed_id": "string",
  "filename": "string",
  "content_type": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/images', json=payload)
data = response.json()
Request Body
{
  "image_data": "string",
  "image_url": "https://example.com",
  "signed_id": "string",
  "filename": "string",
  "content_type": "string"
}
{
  "media_kind": "image",
  "media_url": "https://example.com",
  "image_url": "https://example.com",
  "signed_id": "string",
  "filename": "string",
  "content_type": "string",
  "byte_size": 0,
  "width": 0,
  "height": 0
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

List all templates (paginated)

GET
https://api.nitrosend.com/v1/my/templates

Returns a summary view with section counts (not full design).

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<TemplateSummary>

Template summaries

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List all templates (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/templates'
const response = await fetch('https://api.nitrosend.com/v1/my/templates', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/templates')
data = response.json()
200
[
  {
    "id": 0,
    "name": "string",
    "version": 0,
    "subject": "string",
    "preheader": "string",
    "flow_id": 0,
    "section_count": 0,
    "section_types": [
      "string"
    ],
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Create a standalone template

POST
https://api.nitrosend.com/v1/my/templates

Body

application/json
namestringrequired
subjectstring
preheaderstring
generation_idinteger | null
if_versioninteger

Required optimistic concurrency token. Use the current template.version.

designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring

Response

201CreatedTemplate

Created template

409ConflictError

Template version conflict

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a standalone template
curl -X POST 'https://api.nitrosend.com/v1/my/templates' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "subject": "string",
    "preheader": "string",
    "generation_id": 0,
    "if_version": 0,
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/templates', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "subject": "string",
      "preheader": "string",
      "generation_id": 0,
      "if_version": 0,
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "subject": "string",
  "preheader": "string",
  "generation_id": 0,
  "if_version": 0,
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/templates', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "subject": "string",
  "preheader": "string",
  "generation_id": 0,
  "if_version": 0,
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}
{
  "id": 0,
  "name": "string",
  "flow_id": 0,
  "action_id": 0,
  "version": 0,
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "from_name": "string",
  "from_email": "string",
  "reply_to": "string",
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "variables": {},
  "generation_id": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a template with full design

GET
https://api.nitrosend.com/v1/my/templates/{id}

Parameters

idintegerrequiredpath

Response

200OKTemplate

Full template including design

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a template with full design
curl -X GET 'https://api.nitrosend.com/v1/my/templates/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/templates/{id}')
data = response.json()
{
  "id": 0,
  "name": "string",
  "flow_id": 0,
  "action_id": 0,
  "version": 0,
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "from_name": "string",
  "from_email": "string",
  "reply_to": "string",
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "variables": {},
  "generation_id": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete an unused standalone template

DELETE
https://api.nitrosend.com/v1/my/templates/{id}

Parameters

idintegerrequiredpath
if_versionintegerrequiredquery

Required optimistic concurrency token. Use the current template.version.

Response

200OKobject

Template deleted

404Not FoundError

Resource not found

409ConflictError

Template version conflict or deletion blocked by message/flow history

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete an unused standalone template
curl -X DELETE 'https://api.nitrosend.com/v1/my/templates/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/templates/{id}')
data = response.json()
{
  "deleted": true,
  "template_id": 0
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update a template

PATCH
https://api.nitrosend.com/v1/my/templates/{id}

Body

application/json
namestring
subjectstring
bodystring
preheaderstring
if_versionintegerrequired

Required optimistic concurrency token. Use the current template.version. A stale value returns 409 with current_version and expected_version.

from_namestring
from_emailstring<email>
reply_tostring<email>
generation_idinteger | null
designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring

Parameters

idintegerrequiredpath

Response

200OKTemplate

Updated template

409ConflictError

Template version conflict

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a template
curl -X PATCH 'https://api.nitrosend.com/v1/my/templates/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "if_version": 0,
    "from_name": "string",
    "from_email": "user@example.com",
    "reply_to": "user@example.com",
    "generation_id": 0,
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "if_version": 0,
      "from_name": "string",
      "from_email": "user@example.com",
      "reply_to": "user@example.com",
      "generation_id": 0,
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "if_version": 0,
  "from_name": "string",
  "from_email": "user@example.com",
  "reply_to": "user@example.com",
  "generation_id": 0,
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}

response = requests.patch('https://api.nitrosend.com/v1/my/templates/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "if_version": 0,
  "from_name": "string",
  "from_email": "user@example.com",
  "reply_to": "user@example.com",
  "generation_id": 0,
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}
{
  "id": 0,
  "name": "string",
  "flow_id": 0,
  "action_id": 0,
  "version": 0,
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "from_name": "string",
  "from_email": "string",
  "reply_to": "string",
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "variables": {},
  "generation_id": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Render an existing template to HTML

GET
https://api.nitrosend.com/v1/my/templates/{id}/render

Renders the template through the shared preview renderer.

Parameters

idintegerrequiredpath

Response

200OKobject

Rendered HTML

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Render an existing template to HTML
curl -X GET 'https://api.nitrosend.com/v1/my/templates/{id}/render'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/{id}/render', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/templates/{id}/render')
data = response.json()
{
  "html": "string",
  "accessibility": {
    "valid": true,
    "warnings": [
      {
        "level": "warning",
        "rule": "image_alt_text",
        "message": "string",
        "suggested_fix": "string",
        "count": 0,
        "min_ratio": 0
      }
    ]
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Send a test email

POST
https://api.nitrosend.com/v1/my/templates/{id}/send_test

Send a test email for the given template. Provide email for an explicit recipient, contact_id to use a contact's email (with merge-tag personalization), or omit both to send to the account owner.

Body

application/json
emailstring<email>
contact_idinteger

Parameters

idintegerrequiredpath

Response

200OKobject

Test email sent

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Send a test email
curl -X POST 'https://api.nitrosend.com/v1/my/templates/{id}/send_test' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "user@example.com",
    "contact_id": 0
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/{id}/send_test', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "email": "user@example.com",
      "contact_id": 0
    }),
});

const data = await response.json();
import requests

payload = {
  "email": "user@example.com",
  "contact_id": 0
}

response = requests.post('https://api.nitrosend.com/v1/my/templates/{id}/send_test', json=payload)
data = response.json()
Request Body
{
  "email": "user@example.com",
  "contact_id": 0
}
{
  "sent": 0,
  "results": [
    {
      "email": "string",
      "success": true
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Render an email design to HTML

POST
https://api.nitrosend.com/v1/my/templates/preview

Body

application/json
documentEmailDesignrequired

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring

Response

200OKobject

Rendered HTML

400Bad RequestError

Bad request

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Render an email design to HTML
curl -X POST 'https://api.nitrosend.com/v1/my/templates/preview' \
  -H 'Content-Type: application/json' \
  -d '{
    "document": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/preview', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "document": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "document": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/templates/preview', json=payload)
data = response.json()
Request Body
{
  "document": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  }
}
{
  "html": "string",
  "accessibility": {
    "valid": true,
    "warnings": [
      {
        "level": "warning",
        "rule": "image_alt_text",
        "message": "string",
        "suggested_fix": "string",
        "count": 0,
        "min_ratio": 0
      }
    ]
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Get the email component schema

GET
https://api.nitrosend.com/v1/my/templates/spec

Returns the full schema for email design sections including all component types, their props, required fields, and defaults. Sourced from config/email_components.yml.

Response

200OKEmailComponentSpec

Email component schema

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get the email component schema
curl -X GET 'https://api.nitrosend.com/v1/my/templates/spec'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/spec', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/templates/spec')
data = response.json()
200
{
  "version": 0,
  "design_guidelines": "string",
  "components": [
    {
      "type": "string",
      "description": "string",
      "tips": [
        "string"
      ],
      "props": {}
    }
  ],
  "style_attributes": [
    {
      "key": "string",
      "label": "string",
      "type": "color",
      "theme_fallback": "string",
      "description": "string",
      "values": [
        "string"
      ],
      "target": {
        "el": "section",
        "attr": "string"
      }
    }
  ],
  "variables": [
    "string"
  ],
  "theme_attributes": [
    {
      "key": "string",
      "label": "string",
      "type": "color",
      "category": "color",
      "slot": "string",
      "surfaces": [
        "string"
      ],
      "min": 0,
      "max": 0,
      "values": [
        "string"
      ],
      "options": [
        {
          "value": "string",
          "label": "string",
          "description": "string"
        }
      ],
      "transform_table": {},
      "column": true,
      "value_resolver": "string",
      "description": "string"
    }
  ]
}

List email starter designs

GET
https://api.nitrosend.com/v1/my/templates/starters

Returns config-based starter email designs with the current brand's theme merged and an HTML preview rendered for each starter.

Response

200OKArray<EmailStarter>

Starter designs with brand theme and preview HTML

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List email starter designs
curl -X GET 'https://api.nitrosend.com/v1/my/templates/starters'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/starters', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/templates/starters')
data = response.json()
200
[
  {
    "id": "string",
    "category": "string",
    "name": "string",
    "description": "string",
    "icon": "string",
    "suggested_goals": [
      "string"
    ],
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "preview_html": "string"
  }
]

AI-generate an email template

POST
https://api.nitrosend.com/v1/my/templates/generate

Generate a complete email design from a text goal using AI. Returns a sections-based design, subject line, preheader, and inferred category. Supports refine mode by passing existing sections[] for modification.

Body

application/json
goalstringrequired

What the email should accomplish

categorystring

Optional category hint (welcome, newsletter, promotion, etc.)

tonestring

Optional tone override (formal, casual, etc.)

sectionsArray<object>

Current sections for refine mode — LLM adjusts existing content

Response

200OKobject

Generated email design

422Unprocessable EntityError

Validation error

429Too Many RequestsError

Rate limit exceeded

503Service UnavailableError

Generation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

AI-generate an email template
curl -X POST 'https://api.nitrosend.com/v1/my/templates/generate' \
  -H 'Content-Type: application/json' \
  -d '{
    "goal": "Welcome new subscribers and introduce the brand",
    "category": "string",
    "tone": "string",
    "sections": [
      {}
    ]
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/templates/generate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "goal": "Welcome new subscribers and introduce the brand",
      "category": "string",
      "tone": "string",
      "sections": [
        {}
      ]
    }),
});

const data = await response.json();
import requests

payload = {
  "goal": "Welcome new subscribers and introduce the brand",
  "category": "string",
  "tone": "string",
  "sections": [
    {}
  ]
}

response = requests.post('https://api.nitrosend.com/v1/my/templates/generate', json=payload)
data = response.json()
Request Body
{
  "goal": "Welcome new subscribers and introduce the brand",
  "category": "string",
  "tone": "string",
  "sections": [
    {}
  ]
}
{
  "design": {},
  "subject": "string",
  "preheader": "string",
  "category": "string",
  "prompt_version": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Flows

Automation flows and step schema

List automation flows (paginated)

GET
https://api.nitrosend.com/v1/my/flows

Returns standalone flows only (excludes campaign-attached flows).

Parameters

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<Flow>

Paginated flows

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List automation flows (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/flows'
const response = await fetch('https://api.nitrosend.com/v1/my/flows', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/flows')
data = response.json()
200
[]

Create a flow

POST
https://api.nitrosend.com/v1/my/flows

Body

application/json
namestringrequired
statusstringdraftlivepausedarchivedcancelled
triggerFlowTriggerInput
Show child attributes
eventstring

Built-in: contact_add, keyword, message, list_add, list_remove, product_view, checkout, cart_add, cart_remove, cart_abandoned, browse_abandoned. Custom: any lowercase alphanumeric with underscores.

audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target; null means no audience selected.

segment_idinteger | null
contact_list_idinteger | nulldeprecated

Deprecated — use contact_list_ids

contact_list_idsArray<integer>

Contact list IDs to target

exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set; pass [] to clear

dataobject
stepsArray<FlowStepInput>

Response

201CreatedFlow

Flow created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a flow
curl -X POST 'https://api.nitrosend.com/v1/my/flows' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "status": "draft",
    "trigger": {
      "event": "string",
      "audience_type": "lists",
      "segment_id": 0,
      "contact_list_id": 0,
      "contact_list_ids": [
        0
      ],
      "exclude_segment_ids": [
        0
      ],
      "data": {}
    },
    "steps": []
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/flows', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "status": "draft",
      "trigger": {
        "event": "string",
        "audience_type": "lists",
        "segment_id": 0,
        "contact_list_id": 0,
        "contact_list_ids": [
          0
        ],
        "exclude_segment_ids": [
          0
        ],
        "data": {}
      },
      "steps": []
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "status": "draft",
  "trigger": {
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "data": {}
  },
  "steps": []
}

response = requests.post('https://api.nitrosend.com/v1/my/flows', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "status": "draft",
  "trigger": {
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "data": {}
  },
  "steps": []
}
422
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a flow

GET
https://api.nitrosend.com/v1/my/flows/{id}

Parameters

idintegerrequiredpath

Response

200OKFlow

Flow with full graph

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a flow
curl -X GET 'https://api.nitrosend.com/v1/my/flows/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/flows/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/flows/{id}')
data = response.json()
404
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a flow

DELETE
https://api.nitrosend.com/v1/my/flows/{id}

Parameters

idintegerrequiredpath

Response

200OKFlow

Deleted flow

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a flow
curl -X DELETE 'https://api.nitrosend.com/v1/my/flows/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/flows/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/flows/{id}')
data = response.json()

Update a flow

PATCH
https://api.nitrosend.com/v1/my/flows/{id}

Supports optimistic concurrency — pass updated_at to reject the update if the flow was modified externally.

Body

application/json
namestring
statusstringdraftlivepausedarchivedcancelled
updated_atstring<date-time>

Optimistic concurrency check

triggerFlowTriggerInput
Show child attributes
eventstring

Built-in: contact_add, keyword, message, list_add, list_remove, product_view, checkout, cart_add, cart_remove, cart_abandoned, browse_abandoned. Custom: any lowercase alphanumeric with underscores.

audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target; null means no audience selected.

segment_idinteger | null
contact_list_idinteger | nulldeprecated

Deprecated — use contact_list_ids

contact_list_idsArray<integer>

Contact list IDs to target

exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set; pass [] to clear

dataobject
stepsArray<FlowStepInput>

Parameters

idintegerrequiredpath

Response

200OKFlow

Updated flow

409ConflictError

Conflict — flow was modified externally, or live activation was already accepted (duplicate_flow_live)

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a flow
curl -X PATCH 'https://api.nitrosend.com/v1/my/flows/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "status": "draft",
    "updated_at": "2024-01-15T09:30:00Z",
    "trigger": {
      "event": "string",
      "audience_type": "lists",
      "segment_id": 0,
      "contact_list_id": 0,
      "contact_list_ids": [
        0
      ],
      "exclude_segment_ids": [
        0
      ],
      "data": {}
    },
    "steps": []
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/flows/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "status": "draft",
      "updated_at": "2024-01-15T09:30:00Z",
      "trigger": {
        "event": "string",
        "audience_type": "lists",
        "segment_id": 0,
        "contact_list_id": 0,
        "contact_list_ids": [
          0
        ],
        "exclude_segment_ids": [
          0
        ],
        "data": {}
      },
      "steps": []
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "status": "draft",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "data": {}
  },
  "steps": []
}

response = requests.patch('https://api.nitrosend.com/v1/my/flows/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "status": "draft",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "data": {}
  },
  "steps": []
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get the flow step type schema

GET
https://api.nitrosend.com/v1/my/flows/spec

Returns the schema for flow step types, trigger events, and audience filters. Flow steps and triggers are sourced from config/flows.yml; filters are sourced from the audience filter registry with lifecycle_flows added from the canonical lifecycle catalog.

Response

200OKFlowSpec

Flow specification

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get the flow step type schema
curl -X GET 'https://api.nitrosend.com/v1/my/flows/spec'
const response = await fetch('https://api.nitrosend.com/v1/my/flows/spec', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/flows/spec')
data = response.json()
200
{
  "filters": {},
  "triggers": [
    {
      "title": "string",
      "event": "string"
    }
  ],
  "steps": [
    {
      "type": "string",
      "title": "string",
      "summary": "string",
      "params": {}
    }
  ],
  "lifecycle_flows": [
    {
      "id": "string",
      "key": "string",
      "goal": "string",
      "name": "string",
      "description": "string",
      "priority": 0,
      "trigger": {
        "event": "string"
      },
      "trigger_needs": "string",
      "steps": [
        {
          "type": "string",
          "duration": 0,
          "subject": "string",
          "preheader": "string",
          "body": "string",
          "design": {}
        }
      ]
    }
  ]
}

List flow templates

GET
https://api.nitrosend.com/v1/my/flow_templates

Returns all available flow templates (static, global — not account-scoped). Each entry includes lean card data and preview_sections from the first email step.

Response

200OKArray<FlowTemplate>

List of flow templates

401UnauthorizedError

Not authenticated

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List flow templates
curl -X GET 'https://api.nitrosend.com/v1/my/flow_templates'
const response = await fetch('https://api.nitrosend.com/v1/my/flow_templates', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/flow_templates')
data = response.json()
[
  {
    "id": "string",
    "type_label": "string",
    "description": "string",
    "category": "string",
    "email_count": 0,
    "step_count": 0,
    "preview_sections": [
      {}
    ]
  }
]
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Get a flow template

GET
https://api.nitrosend.com/v1/my/flow_templates/{id}

Returns a single flow template including the full trigger and steps graph, ready to pass directly to POST /v1/my/flows.

Parameters

idstringrequiredpath

Flow template slug (e.g. welcome_series)

Response

200OKFlowTemplate & object

Flow template with full graph

401UnauthorizedError

Not authenticated

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a flow template
curl -X GET 'https://api.nitrosend.com/v1/my/flow_templates/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/flow_templates/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/flow_templates/{id}')
data = response.json()
{
  "id": "string",
  "type_label": "string",
  "description": "string",
  "category": "string",
  "email_count": 0,
  "step_count": 0,
  "preview_sections": [
    {}
  ],
  "trigger": {},
  "steps": [
    {}
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Events

Contact event tracking

List events (paginated)

GET
https://api.nitrosend.com/v1/my/events

Parameters

pageinteger1query
limitinteger<= 10050query
eventstringquery

Filter by event type

contact_idintegerquery
created_afterstring<date-time>query
created_beforestring<date-time>query
resource_uidstringquery
resource_namestringquery
testbooleanquery

Response

200OKArray<Event>

Paginated events

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List events (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/events'
const response = await fetch('https://api.nitrosend.com/v1/my/events', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/events')
data = response.json()
200
[
  {
    "id": 0,
    "account_id": 0,
    "contact_id": 0,
    "user_id": 0,
    "event": "string",
    "amount": 0,
    "data": {},
    "idempotency_key": "string",
    "resource_uid": "string",
    "resource_name": "string",
    "resource_url": "string",
    "test": true,
    "generated": true,
    "chain_depth": 0,
    "ip": "string",
    "user_agent": "string",
    "browser": "string",
    "os": "string",
    "device_type": "string",
    "referrer": "string",
    "utm_source": "string",
    "utm_medium": "string",
    "utm_term": "string",
    "utm_content": "string",
    "utm_campaign": "string",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Track a contact event

POST
https://api.nitrosend.com/v1/my/events

Requires an idempotency key via the Idempotency-Key header or idempotency_key body param. Duplicate events (same account + event type + idempotency key) return the existing event.

Body

application/json
eventstringrequired

Event type name (lowercase, underscores)

contact_idinteger
contact_emailstring<email>

Alternative to contact_id — resolves contact by email

idempotency_keystring

Idempotency key (alternative to header)

amountnumber<double>
resource_uidstring
resource_namestring
resource_urlstring<uri>
testbooleanfalse
dataobject

Custom event payload (max 32KB)

utm_sourcestring
utm_mediumstring
utm_termstring
utm_contentstring
utm_campaignstring

Parameters

Idempotency-Keystringheader

Idempotency key (alternative to body param)

Response

200OKEvent

Duplicate event (idempotent — returns existing)

201CreatedEvent

Event created

400Bad RequestError

Bad request

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Track a contact event
curl -X POST 'https://api.nitrosend.com/v1/my/events' \
  -H 'Content-Type: application/json' \
  -d '{
    "event": "string",
    "contact_id": 0,
    "contact_email": "user@example.com",
    "idempotency_key": "string",
    "amount": 0,
    "resource_uid": "string",
    "resource_name": "string",
    "resource_url": "https://example.com",
    "test": false,
    "data": {},
    "utm_source": "string",
    "utm_medium": "string",
    "utm_term": "string",
    "utm_content": "string",
    "utm_campaign": "string"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/events', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "event": "string",
      "contact_id": 0,
      "contact_email": "user@example.com",
      "idempotency_key": "string",
      "amount": 0,
      "resource_uid": "string",
      "resource_name": "string",
      "resource_url": "https://example.com",
      "test": false,
      "data": {},
      "utm_source": "string",
      "utm_medium": "string",
      "utm_term": "string",
      "utm_content": "string",
      "utm_campaign": "string"
    }),
});

const data = await response.json();
import requests

payload = {
  "event": "string",
  "contact_id": 0,
  "contact_email": "user@example.com",
  "idempotency_key": "string",
  "amount": 0,
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "https://example.com",
  "test": False,
  "data": {},
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string"
}

response = requests.post('https://api.nitrosend.com/v1/my/events', json=payload)
data = response.json()
Request Body
{
  "event": "string",
  "contact_id": 0,
  "contact_email": "user@example.com",
  "idempotency_key": "string",
  "amount": 0,
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "https://example.com",
  "test": false,
  "data": {},
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string"
}
{
  "id": 0,
  "account_id": 0,
  "contact_id": 0,
  "user_id": 0,
  "event": "string",
  "amount": 0,
  "data": {},
  "idempotency_key": "string",
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "string",
  "test": true,
  "generated": true,
  "chain_depth": 0,
  "ip": "string",
  "user_agent": "string",
  "browser": "string",
  "os": "string",
  "device_type": "string",
  "referrer": "string",
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "id": 0,
  "account_id": 0,
  "contact_id": 0,
  "user_id": 0,
  "event": "string",
  "amount": 0,
  "data": {},
  "idempotency_key": "string",
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "string",
  "test": true,
  "generated": true,
  "chain_depth": 0,
  "ip": "string",
  "user_agent": "string",
  "browser": "string",
  "os": "string",
  "device_type": "string",
  "referrer": "string",
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

List event names for filter autocomplete

GET
https://api.nitrosend.com/v1/my/events/names

Returns known platform event names plus observed event names for the current brand.

Response

200OKobject

Event name suggestions

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List event names for filter autocomplete
curl -X GET 'https://api.nitrosend.com/v1/my/events/names'
const response = await fetch('https://api.nitrosend.com/v1/my/events/names', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/events/names')
data = response.json()
200
{
  "names": [
    "string"
  ]
}

Get an event

GET
https://api.nitrosend.com/v1/my/events/{id}

Parameters

idintegerrequiredpath

Response

200OKEvent

Event

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get an event
curl -X GET 'https://api.nitrosend.com/v1/my/events/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/events/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/events/{id}')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "contact_id": 0,
  "user_id": 0,
  "event": "string",
  "amount": 0,
  "data": {},
  "idempotency_key": "string",
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "string",
  "test": true,
  "generated": true,
  "chain_depth": 0,
  "ip": "string",
  "user_agent": "string",
  "browser": "string",
  "os": "string",
  "device_type": "string",
  "referrer": "string",
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete an event

DELETE
https://api.nitrosend.com/v1/my/events/{id}

Parameters

idintegerrequiredpath

Response

200OKEvent

Deleted event

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete an event
curl -X DELETE 'https://api.nitrosend.com/v1/my/events/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/events/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/events/{id}')
data = response.json()
200
{
  "id": 0,
  "account_id": 0,
  "contact_id": 0,
  "user_id": 0,
  "event": "string",
  "amount": 0,
  "data": {},
  "idempotency_key": "string",
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "string",
  "test": true,
  "generated": true,
  "chain_depth": 0,
  "ip": "string",
  "user_agent": "string",
  "browser": "string",
  "os": "string",
  "device_type": "string",
  "referrer": "string",
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Domains

Sending domain verification

List sending domains (paginated)

GET
https://api.nitrosend.com/v1/my/domains

Parameters

pageinteger1query
perinteger<= 10030query

Response

200OKArray<Domain>

Paginated domains

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List sending domains (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/domains'
const response = await fetch('https://api.nitrosend.com/v1/my/domains', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/domains')
data = response.json()
200
[
  {
    "id": 0,
    "brand_id": 0,
    "name": "string",
    "provider": "ses",
    "integration_id": 0,
    "status": "pending",
    "dmarc_policy": "none",
    "dmarc_recommended_policy": "none",
    "dmarc_observed_policy": "none",
    "dns_records": [
      {
        "record_type": "string",
        "name": "string",
        "value": "string",
        "priority": "string",
        "valid": "string"
      }
    ],
    "dns_health": {},
    "dns_setup_status": "unchecked",
    "verified_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z"
  }
]

Add a sending domain

POST
https://api.nitrosend.com/v1/my/domains

Initiates domain verification. Returns DNS records that must be added at your domain registrar before calling POST /verify.

Body

application/json
domainobjectrequired
Show child attributes
namestringrequired

e.g. send.example.com

Response

201CreatedDomain

Domain registered with DNS records to configure

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Add a sending domain
curl -X POST 'https://api.nitrosend.com/v1/my/domains' \
  -H 'Content-Type: application/json' \
  -d '{
    "domain": {
      "name": "string"
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/domains', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "domain": {
        "name": "string"
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "domain": {
    "name": "string"
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/domains', json=payload)
data = response.json()
Request Body
{
  "domain": {
    "name": "string"
  }
}
{
  "id": 0,
  "brand_id": 0,
  "name": "string",
  "provider": "ses",
  "integration_id": 0,
  "status": "pending",
  "dmarc_policy": "none",
  "dmarc_recommended_policy": "none",
  "dmarc_observed_policy": "none",
  "dns_records": [
    {
      "record_type": "string",
      "name": "string",
      "value": "string",
      "priority": "string",
      "valid": "string"
    }
  ],
  "dns_health": {},
  "dns_setup_status": "unchecked",
  "verified_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get a domain with DNS records and status

GET
https://api.nitrosend.com/v1/my/domains/{id}

Parameters

idintegerrequiredpath

Response

200OKDomain

Domain

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a domain with DNS records and status
curl -X GET 'https://api.nitrosend.com/v1/my/domains/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/domains/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/domains/{id}')
data = response.json()
{
  "id": 0,
  "brand_id": 0,
  "name": "string",
  "provider": "ses",
  "integration_id": 0,
  "status": "pending",
  "dmarc_policy": "none",
  "dmarc_recommended_policy": "none",
  "dmarc_observed_policy": "none",
  "dns_records": [
    {
      "record_type": "string",
      "name": "string",
      "value": "string",
      "priority": "string",
      "valid": "string"
    }
  ],
  "dns_health": {},
  "dns_setup_status": "unchecked",
  "verified_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Remove a sending domain

DELETE
https://api.nitrosend.com/v1/my/domains/{id}

Parameters

idintegerrequiredpath

Response

204No Content

Domain deleted

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Remove a sending domain
curl -X DELETE 'https://api.nitrosend.com/v1/my/domains/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/domains/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/domains/{id}')
data = response.json()

Verify domain DNS records

POST
https://api.nitrosend.com/v1/my/domains/{id}/verify

Checks if the required DNS records have propagated. If verified, completes the domain_verified onboarding step and enables sending.

Parameters

idintegerrequiredpath

Response

200OKDomain

Domain verification status

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Verify domain DNS records
curl -X POST 'https://api.nitrosend.com/v1/my/domains/{id}/verify'
const response = await fetch('https://api.nitrosend.com/v1/my/domains/{id}/verify', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/domains/{id}/verify')
data = response.json()
200
{
  "id": 0,
  "brand_id": 0,
  "name": "string",
  "provider": "ses",
  "integration_id": 0,
  "status": "pending",
  "dmarc_policy": "none",
  "dmarc_recommended_policy": "none",
  "dmarc_observed_policy": "none",
  "dns_records": [
    {
      "record_type": "string",
      "name": "string",
      "value": "string",
      "priority": "string",
      "valid": "string"
    }
  ],
  "dns_health": {},
  "dns_setup_status": "unchecked",
  "verified_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}

Create an Entri bootstrap session for a pending domain

POST
https://api.nitrosend.com/v1/my/domains/{id}/entri_session

Returns a short-lived Entri JWT plus the DNS record payload needed to launch the Entri modal for a pending domain owned by the current brand.

Parameters

idintegerrequiredpath

Response

200OKEntriSessionResponse

Entri bootstrap payload

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

429Too Many RequestsError

Rate limit exceeded

503Service UnavailableError

Upstream service unavailable

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create an Entri bootstrap session for a pending domain
curl -X POST 'https://api.nitrosend.com/v1/my/domains/{id}/entri_session'
const response = await fetch('https://api.nitrosend.com/v1/my/domains/{id}/entri_session', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/domains/{id}/entri_session')
data = response.json()
{
  "domain": {
    "id": 0,
    "name": "string",
    "status": "pending"
  },
  "entri": {
    "application_id": "string",
    "token": "string",
    "prefilled_domain": "string",
    "user_id": "string",
    "dns_records": [
      {
        "type": "string",
        "host": "string",
        "value": "string",
        "ttl": 0,
        "priority": 0
      }
    ]
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Integrations

BYO provider integration management

List integrations (paginated)

GET
https://api.nitrosend.com/v1/my/integrations

Parameters

categorystringemailsmscrmquery
pageinteger1query
perinteger<= 10030query

Response

200OKArray<Integration>

Paginated integrations

400Bad RequestError

Bad request

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List integrations (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/integrations'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/integrations')
data = response.json()
[
  {
    "id": 0,
    "provider": "mailgun",
    "category": "email",
    "active": true,
    "primary": true,
    "status": "pending",
    "connected_at": "2024-01-15T09:30:00Z",
    "last_tested_at": "2024-01-15T09:30:00Z",
    "error_message": "string",
    "config_summary": {},
    "secret_hints": {},
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Create or connect an email provider integration

POST
https://api.nitrosend.com/v1/my/integrations

Body

application/json
integrationMailgunIntegrationInput | SesIntegrationInput | PostmarkIntegrationInput | ResendIntegrationInput | SendgridIntegrationInputrequired

Response

201CreatedIntegration

Integration created

400Bad RequestError

Bad request

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create or connect an email provider integration
curl -X POST 'https://api.nitrosend.com/v1/my/integrations' \
  -H 'Content-Type: application/json' \
  -d '{
    "integration": {
      "provider": "mailgun",
      "api_key": "string",
      "domain": "string",
      "region": "string",
      "active": true
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "integration": {
        "provider": "mailgun",
        "api_key": "string",
        "domain": "string",
        "region": "string",
        "active": true
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "integration": {
    "provider": "mailgun",
    "api_key": "string",
    "domain": "string",
    "region": "string",
    "active": True
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/integrations', json=payload)
data = response.json()
Request Body
{
  "integration": {
    "provider": "mailgun",
    "api_key": "string",
    "domain": "string",
    "region": "string",
    "active": true
  }
}
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get an integration

GET
https://api.nitrosend.com/v1/my/integrations/{id}

Parameters

idintegerrequiredpath

Response

200OKIntegration

Integration

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get an integration
curl -X GET 'https://api.nitrosend.com/v1/my/integrations/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/integrations/{id}')
data = response.json()
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Disconnect and delete an integration

DELETE
https://api.nitrosend.com/v1/my/integrations/{id}

Parameters

idintegerrequiredpath
confirmbooleanrequiredquery

Must be true to confirm the destructive action.

Response

204No Content

Integration deleted

400Bad RequestError

Bad request

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Disconnect and delete an integration
curl -X DELETE 'https://api.nitrosend.com/v1/my/integrations/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/integrations/{id}')
data = response.json()
400
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update an email provider integration

PATCH
https://api.nitrosend.com/v1/my/integrations/{id}

Updates credentials or operational fields for an existing email provider. The provider cannot be changed once the integration exists.

Body

application/json
integrationMailgunIntegrationInput | SesIntegrationInput | PostmarkIntegrationInput | ResendIntegrationInput | SendgridIntegrationInputrequired

Parameters

idintegerrequiredpath

Response

200OKIntegration

Integration updated

400Bad RequestError

Bad request

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update an email provider integration
curl -X PATCH 'https://api.nitrosend.com/v1/my/integrations/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "integration": {
      "provider": "mailgun",
      "api_key": "string",
      "domain": "string",
      "region": "string",
      "active": true
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "integration": {
        "provider": "mailgun",
        "api_key": "string",
        "domain": "string",
        "region": "string",
        "active": true
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "integration": {
    "provider": "mailgun",
    "api_key": "string",
    "domain": "string",
    "region": "string",
    "active": True
  }
}

response = requests.patch('https://api.nitrosend.com/v1/my/integrations/{id}', json=payload)
data = response.json()
Request Body
{
  "integration": {
    "provider": "mailgun",
    "api_key": "string",
    "domain": "string",
    "region": "string",
    "active": true
  }
}
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Test integration connectivity

POST
https://api.nitrosend.com/v1/my/integrations/{id}/test

Parameters

idintegerrequiredpath

Response

200OKIntegration

Tested integration

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Test integration connectivity
curl -X POST 'https://api.nitrosend.com/v1/my/integrations/{id}/test'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/{id}/test', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/integrations/{id}/test')
data = response.json()
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Set an integration as the primary provider for its category

PATCH
https://api.nitrosend.com/v1/my/integrations/{id}/primary

Parameters

idintegerrequiredpath

Response

200OKIntegration

Integration marked primary

400Bad RequestError

Bad request

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Set an integration as the primary provider for its category
curl -X PATCH 'https://api.nitrosend.com/v1/my/integrations/{id}/primary'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/{id}/primary', {
  method: 'PATCH',
});

const data = await response.json();
import requests

response = requests.patch('https://api.nitrosend.com/v1/my/integrations/{id}/primary')
data = response.json()
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Start Attio OAuth connect flow

POST
https://api.nitrosend.com/v1/my/integrations/attio/connect

Returns an Attio OAuth authorize URL for the authenticated account/brand. The response URL includes a signed state payload and the API callback URI.

Response

200OKobject

OAuth authorize URL

401UnauthorizedError

Not authenticated

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Start Attio OAuth connect flow
curl -X POST 'https://api.nitrosend.com/v1/my/integrations/attio/connect'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/attio/connect', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/integrations/attio/connect')
data = response.json()
{
  "authorize_url": "https://example.com"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Start HubSpot OAuth connect flow

POST
https://api.nitrosend.com/v1/my/integrations/hubspot/connect

Returns a HubSpot OAuth authorize URL for the authenticated account/brand. The response URL includes a signed state payload and the API callback URI.

Response

200OKobject

OAuth authorize URL

401UnauthorizedError

Not authenticated

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Start HubSpot OAuth connect flow
curl -X POST 'https://api.nitrosend.com/v1/my/integrations/hubspot/connect'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/hubspot/connect', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/integrations/hubspot/connect')
data = response.json()
{
  "authorize_url": "https://example.com"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Start Shopify OAuth connect flow

POST
https://api.nitrosend.com/v1/my/integrations/shopify/connect

Returns a Shopify OAuth authorize URL for the authenticated account/brand. The request accepts a myshopify.com shop domain or shop slug. The response URL includes a signed state payload and the API callback URI.

Body

application/json
shopifyobjectrequired
Show child attributes
shopstringrequired

Response

200OKobject

OAuth authorize URL

401UnauthorizedError

Not authenticated

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Start Shopify OAuth connect flow
curl -X POST 'https://api.nitrosend.com/v1/my/integrations/shopify/connect' \
  -H 'Content-Type: application/json' \
  -d '{
    "shopify": {
      "shop": "test-shop.myshopify.com"
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/shopify/connect', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "shopify": {
        "shop": "test-shop.myshopify.com"
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "shopify": {
    "shop": "test-shop.myshopify.com"
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/integrations/shopify/connect', json=payload)
data = response.json()
Request Body
{
  "shopify": {
    "shop": "test-shop.myshopify.com"
  }
}
{
  "authorize_url": "https://example.com"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Connect customer-data Stripe integration

POST
https://api.nitrosend.com/v1/my/integrations/stripe/connect

Connects a customer's own Stripe account using a Restricted API Key and a webhook signing secret for /hooks/stripe_data. This is isolated from Nitrosend billing webhooks at /hooks/stripe.

Body

application/json
stripeobjectrequired
Show child attributes
api_keystringrequired

Customer Stripe Restricted API Key beginning with rk_.

signing_secretstringrequired

Stripe webhook signing secret for /hooks/stripe_data.

Response

200OKobject

Stripe integration connected

401UnauthorizedError

Not authenticated

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Connect customer-data Stripe integration
curl -X POST 'https://api.nitrosend.com/v1/my/integrations/stripe/connect' \
  -H 'Content-Type: application/json' \
  -d '{
    "stripe": {
      "api_key": "string",
      "signing_secret": "string"
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/integrations/stripe/connect', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "stripe": {
        "api_key": "string",
        "signing_secret": "string"
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "stripe": {
    "api_key": "string",
    "signing_secret": "string"
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/integrations/stripe/connect', json=payload)
data = response.json()
Request Body
{
  "stripe": {
    "api_key": "string",
    "signing_secret": "string"
  }
}
{
  "integration_id": 0,
  "status": "string",
  "provider": "stripe"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Attio OAuth callback endpoint

GET
https://api.nitrosend.com/integrations/attio/callback

Public callback endpoint used by Attio after user authorization. Verifies signed state, exchanges auth code, and finalizes Attio integration connection.

Parameters

codestringquery
statestringquery
errorstringquery
error_descriptionstringquery

Response

200OKstring

OAuth callback processed (HTML)

400Bad Requeststring

Invalid callback request

422Unprocessable Entitystring

OAuth exchange or webhook provisioning failure

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Attio OAuth callback endpoint
curl -X GET 'https://api.nitrosend.com/integrations/attio/callback'
const response = await fetch('https://api.nitrosend.com/integrations/attio/callback', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/integrations/attio/callback')
data = response.json()
"string"
"string"
"string"

HubSpot OAuth callback endpoint

GET
https://api.nitrosend.com/integrations/hubspot/callback

Public callback endpoint used by HubSpot after user authorization. Verifies signed state, exchanges auth code, persists the integration, and enqueues initial sync.

Parameters

codestringquery
statestringquery
errorstringquery
error_descriptionstringquery

Response

200OKstring

OAuth callback processed (HTML)

400Bad Requeststring

Invalid callback request

422Unprocessable Entitystring

OAuth exchange failure

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

HubSpot OAuth callback endpoint
curl -X GET 'https://api.nitrosend.com/integrations/hubspot/callback'
const response = await fetch('https://api.nitrosend.com/integrations/hubspot/callback', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/integrations/hubspot/callback')
data = response.json()
"string"
"string"
"string"

Shopify OAuth callback endpoint

GET
https://api.nitrosend.com/integrations/shopify/callback

Public callback endpoint used by Shopify after user authorization. Verifies signed state, shop domain, Shopify OAuth HMAC, exchanges the auth code, persists the integration, and enqueues initial sync.

Parameters

codestringquery
statestringquery
shopstringquery
hmacstringquery
timestampstringquery
errorstringquery
error_descriptionstringquery

Response

200OKstring

OAuth callback processed (HTML)

400Bad Requeststring

Invalid callback request

422Unprocessable Entitystring

OAuth exchange failure

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Shopify OAuth callback endpoint
curl -X GET 'https://api.nitrosend.com/integrations/shopify/callback'
const response = await fetch('https://api.nitrosend.com/integrations/shopify/callback', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/integrations/shopify/callback')
data = response.json()
"string"
"string"
"string"

Brands

Brand context plus Brand Kit identity, theme, and per-brand configuration

List brands (paginated)

GET
https://api.nitrosend.com/v1/my/brands

Parameters

pageinteger1query
perinteger<= 100100query

Response

200OKArray<Brand>

All brands for the account

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List brands (paginated)
curl -X GET 'https://api.nitrosend.com/v1/my/brands'
const response = await fetch('https://api.nitrosend.com/v1/my/brands', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/brands')
data = response.json()
200
[
  {
    "id": 0,
    "sid": "string",
    "account_id": 0,
    "brand_color": "string",
    "text_color": "string",
    "bg_color": "string",
    "radius": 0,
    "spacing_density": "compact",
    "font_heading": "string",
    "font_body": "string",
    "brand_document": "string",
    "style_notes": "string",
    "tone": "string",
    "company_description": "string",
    "industry": "string",
    "example_copy": [
      "string"
    ],
    "default_header": {},
    "default_footer": {},
    "default_theme": {},
    "physical_address": "string",
    "company_name": "string",
    "source_url": "string",
    "last_scraped_at": "2024-01-15T09:30:00Z",
    "links": [
      {
        "url": "string",
        "icon": "string",
        "title": "string"
      }
    ],
    "logo": "string",
    "complete": true,
    "email_from_name": "string",
    "email_from_email": "string",
    "email_reply_to": "string",
    "email_view_online": true,
    "test_email_recipients": [
      "user@example.com"
    ],
    "onboarding_state": {},
    "onboarding": {
      "steps": {},
      "progress": {
        "completed": 0,
        "total": 0
      }
    },
    "domain_verified": true,
    "can_send": true,
    "byo_routing": {
      "mismatch": true,
      "provider": "string",
      "bypassing_domains": [
        "string"
      ],
      "message": "string"
    },
    "subscribed_contacts_count": 0,
    "using_sandbox": true,
    "sandbox_email": "string",
    "sandbox_monthly_cap": 0,
    "sandbox_sends_remaining": 0,
    "capabilities": {},
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Create a brand

POST
https://api.nitrosend.com/v1/my/brands

Body

application/json
namestring

Internal brand name; mirrors company_name when company_name is omitted

brand_colorstring
text_colorstring
bg_colorstring
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring
font_bodystring
heading_sizeinteger | null[12, 48]
body_sizeinteger | null[12, 20]
brand_documentstring | null
style_notesstring
tonestring
company_descriptionstring
industrystring
physical_addressstring
company_namestring
logostring

Signed blob ID or URL

email_from_namestring
email_from_emailstring<email>
email_reply_tostring<email>
email_view_onlineboolean
test_email_recipientsArray<string>
example_copyArray<string>
linksArray<object>
Show child attributes
urlstring<uri>
iconstring
titlestring
default_headerobject
default_footerobject
default_themeobject

Response

201CreatedBrand

Brand created

422Unprocessable Entityobject | Error & object

Brand limit reached or validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a brand
curl -X POST 'https://api.nitrosend.com/v1/my/brands' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "brand_color": "string",
    "text_color": "string",
    "bg_color": "string",
    "radius": 0,
    "spacing_density": "compact",
    "font_heading": "string",
    "font_body": "string",
    "heading_size": 12,
    "body_size": 12,
    "brand_document": "string",
    "style_notes": "string",
    "tone": "string",
    "company_description": "string",
    "industry": "string",
    "physical_address": "string",
    "company_name": "string",
    "logo": "string",
    "email_from_name": "string",
    "email_from_email": "user@example.com",
    "email_reply_to": "user@example.com",
    "email_view_online": true,
    "test_email_recipients": [
      "user@example.com"
    ],
    "example_copy": [
      "string"
    ],
    "links": [
      {
        "url": "https://example.com",
        "icon": "string",
        "title": "string"
      }
    ],
    "default_header": {},
    "default_footer": {},
    "default_theme": {}
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/brands', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "brand_color": "string",
      "text_color": "string",
      "bg_color": "string",
      "radius": 0,
      "spacing_density": "compact",
      "font_heading": "string",
      "font_body": "string",
      "heading_size": 12,
      "body_size": 12,
      "brand_document": "string",
      "style_notes": "string",
      "tone": "string",
      "company_description": "string",
      "industry": "string",
      "physical_address": "string",
      "company_name": "string",
      "logo": "string",
      "email_from_name": "string",
      "email_from_email": "user@example.com",
      "email_reply_to": "user@example.com",
      "email_view_online": true,
      "test_email_recipients": [
        "user@example.com"
      ],
      "example_copy": [
        "string"
      ],
      "links": [
        {
          "url": "https://example.com",
          "icon": "string",
          "title": "string"
        }
      ],
      "default_header": {},
      "default_footer": {},
      "default_theme": {}
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "heading_size": 12,
  "body_size": 12,
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "physical_address": "string",
  "company_name": "string",
  "logo": "string",
  "email_from_name": "string",
  "email_from_email": "user@example.com",
  "email_reply_to": "user@example.com",
  "email_view_online": True,
  "test_email_recipients": [
    "user@example.com"
  ],
  "example_copy": [
    "string"
  ],
  "links": [
    {
      "url": "https://example.com",
      "icon": "string",
      "title": "string"
    }
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {}
}

response = requests.post('https://api.nitrosend.com/v1/my/brands', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "heading_size": 12,
  "body_size": 12,
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "physical_address": "string",
  "company_name": "string",
  "logo": "string",
  "email_from_name": "string",
  "email_from_email": "user@example.com",
  "email_reply_to": "user@example.com",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "example_copy": [
    "string"
  ],
  "links": [
    {
      "url": "https://example.com",
      "icon": "string",
      "title": "string"
    }
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {}
}
{
  "id": 0,
  "sid": "string",
  "account_id": 0,
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "example_copy": [
    "string"
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {},
  "physical_address": "string",
  "company_name": "string",
  "source_url": "string",
  "last_scraped_at": "2024-01-15T09:30:00Z",
  "links": [
    {
      "url": "string",
      "icon": "string",
      "title": "string"
    }
  ],
  "logo": "string",
  "complete": true,
  "email_from_name": "string",
  "email_from_email": "string",
  "email_reply_to": "string",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "onboarding_state": {},
  "onboarding": {
    "steps": {},
    "progress": {
      "completed": 0,
      "total": 0
    }
  },
  "domain_verified": true,
  "can_send": true,
  "byo_routing": {
    "mismatch": true,
    "provider": "string",
    "bypassing_domains": [
      "string"
    ],
    "message": "string"
  },
  "subscribed_contacts_count": 0,
  "using_sandbox": true,
  "sandbox_email": "string",
  "sandbox_monthly_cap": 0,
  "sandbox_sends_remaining": 0,
  "capabilities": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "error": true,
  "code": "brand_limit_reached",
  "message": "string"
}

Get a brand

GET
https://api.nitrosend.com/v1/my/brands/{sid}

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKBrand

Brand

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a brand
curl -X GET 'https://api.nitrosend.com/v1/my/brands/{sid}'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/brands/{sid}')
data = response.json()
{
  "id": 0,
  "sid": "string",
  "account_id": 0,
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "example_copy": [
    "string"
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {},
  "physical_address": "string",
  "company_name": "string",
  "source_url": "string",
  "last_scraped_at": "2024-01-15T09:30:00Z",
  "links": [
    {
      "url": "string",
      "icon": "string",
      "title": "string"
    }
  ],
  "logo": "string",
  "complete": true,
  "email_from_name": "string",
  "email_from_email": "string",
  "email_reply_to": "string",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "onboarding_state": {},
  "onboarding": {
    "steps": {},
    "progress": {
      "completed": 0,
      "total": 0
    }
  },
  "domain_verified": true,
  "can_send": true,
  "byo_routing": {
    "mismatch": true,
    "provider": "string",
    "bypassing_domains": [
      "string"
    ],
    "message": "string"
  },
  "subscribed_contacts_count": 0,
  "using_sandbox": true,
  "sandbox_email": "string",
  "sandbox_monthly_cap": 0,
  "sandbox_sends_remaining": 0,
  "capabilities": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Delete a brand

DELETE
https://api.nitrosend.com/v1/my/brands/{sid}

Parameters

sidstringrequiredpath

Brand secure identifier

forcestringtruequery

Must be true to delete a brand that has queued messages or scheduled/live campaigns.

Response

200OKBrand

Deleted brand

409Conflictobject

Brand has active sends and requires explicit force confirmation

422Unprocessable EntityError

Cannot delete the only brand

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a brand
curl -X DELETE 'https://api.nitrosend.com/v1/my/brands/{sid}'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/brands/{sid}')
data = response.json()
{
  "id": 0,
  "sid": "string",
  "account_id": 0,
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "example_copy": [
    "string"
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {},
  "physical_address": "string",
  "company_name": "string",
  "source_url": "string",
  "last_scraped_at": "2024-01-15T09:30:00Z",
  "links": [
    {
      "url": "string",
      "icon": "string",
      "title": "string"
    }
  ],
  "logo": "string",
  "complete": true,
  "email_from_name": "string",
  "email_from_email": "string",
  "email_reply_to": "string",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "onboarding_state": {},
  "onboarding": {
    "steps": {},
    "progress": {
      "completed": 0,
      "total": 0
    }
  },
  "domain_verified": true,
  "can_send": true,
  "byo_routing": {
    "mismatch": true,
    "provider": "string",
    "bypassing_domains": [
      "string"
    ],
    "message": "string"
  },
  "subscribed_contacts_count": 0,
  "using_sandbox": true,
  "sandbox_email": "string",
  "sandbox_monthly_cap": 0,
  "sandbox_sends_remaining": 0,
  "capabilities": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "error": true,
  "code": "brand_has_active_sends",
  "active_sends": {
    "queued_messages": 0,
    "active_campaigns": 0
  },
  "deletion_safety": {
    "deletion_impact": {
      "contacts": 0,
      "campaigns": 0,
      "flows": 0,
      "templates": 0,
      "domains": 0,
      "messages": 0
    },
    "active_sends": {
      "queued_messages": 0,
      "active_campaigns": 0
    },
    "can_delete": true,
    "requires_force": true
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update a brand

PATCH
https://api.nitrosend.com/v1/my/brands/{sid}

Body

application/json
brand_colorstring
text_colorstring
bg_colorstring
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring
font_bodystring
heading_sizeinteger | null[12, 48]
body_sizeinteger | null[12, 20]
brand_documentstring | null
style_notesstring
tonestring
company_descriptionstring
industrystring
physical_addressstring
company_namestring
logostring

Signed blob ID or URL

email_from_namestring
email_from_emailstring<email>
email_reply_tostring<email>
test_email_recipientsArray<string>
example_copyArray<string>
linksArray<object>
Show child attributes
urlstring<uri>
iconstring
titlestring
default_headerobject
default_footerobject
default_themeobject

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKBrand

Updated brand

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a brand
curl -X PATCH 'https://api.nitrosend.com/v1/my/brands/{sid}' \
  -H 'Content-Type: application/json' \
  -d '{
    "brand_color": "string",
    "text_color": "string",
    "bg_color": "string",
    "radius": 0,
    "spacing_density": "compact",
    "font_heading": "string",
    "font_body": "string",
    "heading_size": 12,
    "body_size": 12,
    "brand_document": "string",
    "style_notes": "string",
    "tone": "string",
    "company_description": "string",
    "industry": "string",
    "physical_address": "string",
    "company_name": "string",
    "logo": "string",
    "email_from_name": "string",
    "email_from_email": "user@example.com",
    "email_reply_to": "user@example.com",
    "test_email_recipients": [
      "user@example.com"
    ],
    "example_copy": [
      "string"
    ],
    "links": [
      {
        "url": "https://example.com",
        "icon": "string",
        "title": "string"
      }
    ],
    "default_header": {},
    "default_footer": {},
    "default_theme": {}
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "brand_color": "string",
      "text_color": "string",
      "bg_color": "string",
      "radius": 0,
      "spacing_density": "compact",
      "font_heading": "string",
      "font_body": "string",
      "heading_size": 12,
      "body_size": 12,
      "brand_document": "string",
      "style_notes": "string",
      "tone": "string",
      "company_description": "string",
      "industry": "string",
      "physical_address": "string",
      "company_name": "string",
      "logo": "string",
      "email_from_name": "string",
      "email_from_email": "user@example.com",
      "email_reply_to": "user@example.com",
      "test_email_recipients": [
        "user@example.com"
      ],
      "example_copy": [
        "string"
      ],
      "links": [
        {
          "url": "https://example.com",
          "icon": "string",
          "title": "string"
        }
      ],
      "default_header": {},
      "default_footer": {},
      "default_theme": {}
    }),
});

const data = await response.json();
import requests

payload = {
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "heading_size": 12,
  "body_size": 12,
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "physical_address": "string",
  "company_name": "string",
  "logo": "string",
  "email_from_name": "string",
  "email_from_email": "user@example.com",
  "email_reply_to": "user@example.com",
  "test_email_recipients": [
    "user@example.com"
  ],
  "example_copy": [
    "string"
  ],
  "links": [
    {
      "url": "https://example.com",
      "icon": "string",
      "title": "string"
    }
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {}
}

response = requests.patch('https://api.nitrosend.com/v1/my/brands/{sid}', json=payload)
data = response.json()
Request Body
{
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "heading_size": 12,
  "body_size": 12,
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "physical_address": "string",
  "company_name": "string",
  "logo": "string",
  "email_from_name": "string",
  "email_from_email": "user@example.com",
  "email_reply_to": "user@example.com",
  "test_email_recipients": [
    "user@example.com"
  ],
  "example_copy": [
    "string"
  ],
  "links": [
    {
      "url": "https://example.com",
      "icon": "string",
      "title": "string"
    }
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {}
}
{
  "id": 0,
  "sid": "string",
  "account_id": 0,
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "example_copy": [
    "string"
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {},
  "physical_address": "string",
  "company_name": "string",
  "source_url": "string",
  "last_scraped_at": "2024-01-15T09:30:00Z",
  "links": [
    {
      "url": "string",
      "icon": "string",
      "title": "string"
    }
  ],
  "logo": "string",
  "complete": true,
  "email_from_name": "string",
  "email_from_email": "string",
  "email_reply_to": "string",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "onboarding_state": {},
  "onboarding": {
    "steps": {},
    "progress": {
      "completed": 0,
      "total": 0
    }
  },
  "domain_verified": true,
  "can_send": true,
  "byo_routing": {
    "mismatch": true,
    "provider": "string",
    "bypassing_domains": [
      "string"
    ],
    "message": "string"
  },
  "subscribed_contacts_count": 0,
  "using_sandbox": true,
  "sandbox_email": "string",
  "sandbox_monthly_cap": 0,
  "sandbox_sends_remaining": 0,
  "capabilities": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get deletion safety for a brand

GET
https://api.nitrosend.com/v1/my/brands/{sid}/deletion_safety

Returns authoritative deletion impact counts and active-send state for the confirmation UI.

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKBrandDeletionSafety

Brand deletion safety

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get deletion safety for a brand
curl -X GET 'https://api.nitrosend.com/v1/my/brands/{sid}/deletion_safety'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/deletion_safety', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/brands/{sid}/deletion_safety')
data = response.json()
{
  "deletion_impact": {
    "contacts": 0,
    "campaigns": 0,
    "flows": 0,
    "templates": 0,
    "domains": 0,
    "messages": 0
  },
  "active_sends": {
    "queued_messages": 0,
    "active_campaigns": 0
  },
  "can_delete": true,
  "requires_force": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Auto-detect brand from website

POST
https://api.nitrosend.com/v1/my/brands/{sid}/scrape

Enqueues a background job that scrapes the URL for brand colors, fonts, logo, and tone. Returns 202 immediately. Poll GET /v1/my/brands/{sid} for results.

Body

application/json
urlstring<uri>required

Parameters

sidstringrequiredpath

Brand secure identifier

Response

202Acceptedobject

Scrape job enqueued

422Unprocessable Entityobject

Invalid URL

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Auto-detect brand from website
curl -X POST 'https://api.nitrosend.com/v1/my/brands/{sid}/scrape' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/scrape', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "url": "https://example.com"
    }),
});

const data = await response.json();
import requests

payload = {
  "url": "https://example.com"
}

response = requests.post('https://api.nitrosend.com/v1/my/brands/{sid}/scrape', json=payload)
data = response.json()
Request Body
{
  "url": "https://example.com"
}
{
  "status": "scraping",
  "url": "https://example.com"
}
{
  "error": "string"
}

Get brand onboarding state

GET
https://api.nitrosend.com/v1/my/brands/{sid}/onboarding

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKobject

Onboarding state for the brand

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get brand onboarding state
curl -X GET 'https://api.nitrosend.com/v1/my/brands/{sid}/onboarding'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/onboarding', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/brands/{sid}/onboarding')
data = response.json()
{
  "steps": {},
  "progress": {
    "completed": 0,
    "total": 0
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Complete a brand onboarding step

POST
https://api.nitrosend.com/v1/my/brands/{sid}/onboarding/steps

Body

application/json
stepstringbrand_kit_setupdomain_verifiedfirst_contactfirst_sendrequired

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKobject

Updated onboarding state

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Complete a brand onboarding step
curl -X POST 'https://api.nitrosend.com/v1/my/brands/{sid}/onboarding/steps' \
  -H 'Content-Type: application/json' \
  -d '{
    "step": "brand_kit_setup"
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/onboarding/steps', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "step": "brand_kit_setup"
    }),
});

const data = await response.json();
import requests

payload = {
  "step": "brand_kit_setup"
}

response = requests.post('https://api.nitrosend.com/v1/my/brands/{sid}/onboarding/steps', json=payload)
data = response.json()
Request Body
{
  "step": "brand_kit_setup"
}
{
  "steps": {},
  "progress": {
    "completed": 0,
    "total": 0
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Get brand setup center state

GET
https://api.nitrosend.com/v1/my/brands/{sid}/setup_center

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKSetupCenterPayload

Setup center state for the brand

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get brand setup center state
curl -X GET 'https://api.nitrosend.com/v1/my/brands/{sid}/setup_center'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/setup_center', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/brands/{sid}/setup_center')
data = response.json()
{
  "cards": [
    {
      "id": "brand_kit_scan",
      "complete": true,
      "acknowledged": true,
      "acknowledged_at": "2024-01-15T09:30:00Z"
    }
  ],
  "sections": {
    "required": [
      "string"
    ],
    "recommended": [
      "string"
    ]
  },
  "progress": {
    "completed": 0,
    "total": 0
  },
  "seen": true,
  "dismissed": true,
  "complete": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update brand setup center state

PATCH
https://api.nitrosend.com/v1/my/brands/{sid}/setup_center

Body

application/json

Provide one of seen, dismissed, or card.

seenboolean
dismissedboolean
cardstringbrand_kit_scandnsplanconnect_agentimport_subscribersconnect_transactional
metadataobject

Parameters

sidstringrequiredpath

Brand secure identifier

Response

200OKSetupCenterPayload

Updated setup center state

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update brand setup center state
curl -X PATCH 'https://api.nitrosend.com/v1/my/brands/{sid}/setup_center' \
  -H 'Content-Type: application/json' \
  -d '{
    "seen": true,
    "dismissed": true,
    "card": "brand_kit_scan",
    "metadata": {}
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/brands/{sid}/setup_center', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "seen": true,
      "dismissed": true,
      "card": "brand_kit_scan",
      "metadata": {}
    }),
});

const data = await response.json();
import requests

payload = {
  "seen": True,
  "dismissed": True,
  "card": "brand_kit_scan",
  "metadata": {}
}

response = requests.patch('https://api.nitrosend.com/v1/my/brands/{sid}/setup_center', json=payload)
data = response.json()
Request Body
{
  "seen": true,
  "dismissed": true,
  "card": "brand_kit_scan",
  "metadata": {}
}
{
  "cards": [
    {
      "id": "brand_kit_scan",
      "complete": true,
      "acknowledged": true,
      "acknowledged_at": "2024-01-15T09:30:00Z"
    }
  ],
  "sections": {
    "required": [
      "string"
    ],
    "recommended": [
      "string"
    ]
  },
  "progress": {
    "completed": 0,
    "total": 0
  },
  "seen": true,
  "dismissed": true,
  "complete": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

SavedViews

Saved table presets (filters + sort + columns) per surface

List saved views visible to the current user

GET
https://api.nitrosend.com/v1/my/saved_views

Returns all shared views in the current account plus the caller's own private views. Optionally filtered by surface.

Parameters

surfacestringcontactssendingactivityquery

Filter views by surface

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<SavedView>

Saved views

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List saved views visible to the current user
curl -X GET 'https://api.nitrosend.com/v1/my/saved_views'
const response = await fetch('https://api.nitrosend.com/v1/my/saved_views', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/saved_views')
data = response.json()
200
[]

Create a saved view

POST
https://api.nitrosend.com/v1/my/saved_views

Creates a new saved view owned by the current user. Defaults to private visibility if not specified.

Body

application/json
surfacestringcontactssendingactivityrequired
namestringrequired
visibilitystringprivatesharedprivate
filtersSegmentFilterExpression
layoutSavedViewLayout | null

Table layout preset for contacts surface views. Only present when surface = contacts.

Show child attributes
columnsArray<string>

Ordered list of column keys to display

sortobject
Show child attributes
fieldstring

Column key to sort by

dirstringascdesc

Sort direction

Response

201CreatedSavedView

Saved view created

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create a saved view
curl -X POST 'https://api.nitrosend.com/v1/my/saved_views' \
  -H 'Content-Type: application/json' \
  -d '{
    "surface": "contacts",
    "name": "string",
    "visibility": "private",
    "layout": {
      "columns": [
        "string"
      ],
      "sort": {
        "field": "string",
        "dir": "asc"
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/saved_views', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "surface": "contacts",
      "name": "string",
      "visibility": "private",
      "layout": {
        "columns": [
          "string"
        ],
        "sort": {
          "field": "string",
          "dir": "asc"
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "surface": "contacts",
  "name": "string",
  "visibility": "private",
  "layout": {
    "columns": [
      "string"
    ],
    "sort": {
      "field": "string",
      "dir": "asc"
    }
  }
}

response = requests.post('https://api.nitrosend.com/v1/my/saved_views', json=payload)
data = response.json()
Request Body
{
  "surface": "contacts",
  "name": "string",
  "visibility": "private",
  "layout": {
    "columns": [
      "string"
    ],
    "sort": {
      "field": "string",
      "dir": "asc"
    }
  }
}
422
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Delete a saved view

DELETE
https://api.nitrosend.com/v1/my/saved_views/{id}

Deletes a saved view. Allowed only for the creator or the account owner. Other members receive 403.

Parameters

idintegerrequiredpath

Response

200OKSavedView

Deleted saved view

403ForbiddenError

Not authorized

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Delete a saved view
curl -X DELETE 'https://api.nitrosend.com/v1/my/saved_views/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/saved_views/{id}', {
  method: 'DELETE',
});

const data = await response.json();
import requests

response = requests.delete('https://api.nitrosend.com/v1/my/saved_views/{id}')
data = response.json()
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Update a saved view

PATCH
https://api.nitrosend.com/v1/my/saved_views/{id}

Updates a saved view. Allowed only for the creator or the account owner. Other members receive 403.

Body

application/json
namestring
visibilitystringprivateshared
filtersSegmentFilterExpression
layoutSavedViewLayout | null

Table layout preset for contacts surface views. Only present when surface = contacts.

Show child attributes
columnsArray<string>

Ordered list of column keys to display

sortobject
Show child attributes
fieldstring

Column key to sort by

dirstringascdesc

Sort direction

Parameters

idintegerrequiredpath

Response

200OKSavedView

Updated saved view

403ForbiddenError

Not authorized

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Update a saved view
curl -X PATCH 'https://api.nitrosend.com/v1/my/saved_views/{id}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "string",
    "visibility": "private",
    "layout": {
      "columns": [
        "string"
      ],
      "sort": {
        "field": "string",
        "dir": "asc"
      }
    }
  }'
const response = await fetch('https://api.nitrosend.com/v1/my/saved_views/{id}', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "string",
      "visibility": "private",
      "layout": {
        "columns": [
          "string"
        ],
        "sort": {
          "field": "string",
          "dir": "asc"
        }
      }
    }),
});

const data = await response.json();
import requests

payload = {
  "name": "string",
  "visibility": "private",
  "layout": {
    "columns": [
      "string"
    ],
    "sort": {
      "field": "string",
      "dir": "asc"
    }
  }
}

response = requests.patch('https://api.nitrosend.com/v1/my/saved_views/{id}', json=payload)
data = response.json()
Request Body
{
  "name": "string",
  "visibility": "private",
  "layout": {
    "columns": [
      "string"
    ],
    "sort": {
      "field": "string",
      "dir": "asc"
    }
  }
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Fork a saved view into a new private view

POST
https://api.nitrosend.com/v1/my/saved_views/{id}/fork

Clones an existing view (typically a shared view) into a new private view owned by the caller. The original is not modified.

Parameters

idintegerrequiredpath

Response

201CreatedSavedView

Forked saved view

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Fork a saved view into a new private view
curl -X POST 'https://api.nitrosend.com/v1/my/saved_views/{id}/fork'
const response = await fetch('https://api.nitrosend.com/v1/my/saved_views/{id}/fork', {
  method: 'POST',
});

const data = await response.json();
import requests

response = requests.post('https://api.nitrosend.com/v1/my/saved_views/{id}/fork')
data = response.json()
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Public

Unauthenticated public endpoints

Ingest a Shopify Web Pixel event

POST
https://api.nitrosend.com/v1/public/shopify/events

Browser-safe public collector for Shopify Web Pixel standard events. Requires a dedicated wpkey_live_... collector key. Secret nskey_live_... API keys are rejected. Pixel events are treated as funnel telemetry; verified Shopify webhooks remain authoritative for revenue checkout/purchase Events.

Body

application/json
event_idstringrequired
event_namestringproduct_viewedproduct_added_to_cartproduct_removed_from_cartcheckout_startedcheckout_completedrequired
customer_idstring
emailstring<email>
phonestring
propertiesobject

Response

202Acceptedobject

Event accepted, ignored, or dropped

401UnauthorizedError

Not authenticated

422Unprocessable EntityError & object

Validation failed

Authorization

bearerAuth
Ingest a Shopify Web Pixel event
curl -X POST 'https://api.nitrosend.com/v1/public/shopify/events' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_id": "string",
    "event_name": "product_viewed",
    "customer_id": "string",
    "email": "user@example.com",
    "phone": "string",
    "properties": {}
  }'
const response = await fetch('https://api.nitrosend.com/v1/public/shopify/events', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "event_id": "string",
      "event_name": "product_viewed",
      "customer_id": "string",
      "email": "user@example.com",
      "phone": "string",
      "properties": {}
    }),
});

const data = await response.json();
import requests

payload = {
  "event_id": "string",
  "event_name": "product_viewed",
  "customer_id": "string",
  "email": "user@example.com",
  "phone": "string",
  "properties": {}
}

response = requests.post('https://api.nitrosend.com/v1/public/shopify/events', json=payload)
data = response.json()
Request Body
{
  "event_id": "string",
  "event_name": "product_viewed",
  "customer_id": "string",
  "email": "user@example.com",
  "phone": "string",
  "properties": {}
}
{
  "ok": true,
  "outcome": "captured",
  "event_id": 0
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}

Create or update a public contact (website forms)

POST
https://api.nitrosend.com/v1/public/contacts

Public signup endpoint for website forms. Authenticate with a wpkey_live_… public key. Returns { ok: true } whether the contact is new or already subscribed; this preserves idempotency and avoids leaking which emails are on the list.

Use the brand's public key (wpkey_live_…). Secret keys (nskey_live_…) are rejected on this endpoint.

Body

application/json
emailstring<email>required

Subscriber email address.

list_idintegerrequired

ID of a contact list owned by the brand whose public key is presented.

first_namestring | null

Optional first name written to the contact.

last_namestring | null

Optional last name written to the contact.

phonestring | null

Optional E.164 phone number written to the SMS channel if provided.

sourcestring | null

Optional free-text label, e.g. "footer-form" or "/pricing", recorded against the signup.

dataobject | null

Optional bag of custom contact data. Reserved keys are silently dropped.

Response

201Createdobject

Contact accepted

401UnauthorizedError

Not authenticated

403ForbiddenError

Not authorized

404Not FoundError

Resource not found

422Unprocessable EntityError & object

Validation failed

429Too Many RequestsError

Rate limit exceeded

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Create or update a public contact (website forms)
curl -X POST 'https://api.nitrosend.com/v1/public/contacts' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "user@example.com",
    "list_id": 0,
    "first_name": "string",
    "last_name": "string",
    "phone": "string",
    "source": "string",
    "data": {}
  }'
const response = await fetch('https://api.nitrosend.com/v1/public/contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "email": "user@example.com",
      "list_id": 0,
      "first_name": "string",
      "last_name": "string",
      "phone": "string",
      "source": "string",
      "data": {}
    }),
});

const data = await response.json();
import requests

payload = {
  "email": "user@example.com",
  "list_id": 0,
  "first_name": "string",
  "last_name": "string",
  "phone": "string",
  "source": "string",
  "data": {}
}

response = requests.post('https://api.nitrosend.com/v1/public/contacts', json=payload)
data = response.json()
Request Body
{
  "email": "user@example.com",
  "list_id": 0,
  "first_name": "string",
  "last_name": "string",
  "phone": "string",
  "source": "string",
  "data": {}
}
{
  "ok": true
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string",
  "validation_errors": {}
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Get available plans

GET
https://api.nitrosend.com/v1/app

Response

200OKobject

Active plans

Get available plans
curl -X GET 'https://api.nitrosend.com/v1/app'
const response = await fetch('https://api.nitrosend.com/v1/app', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/app')
data = response.json()
200
{
  "plans": [
    {
      "id": 0,
      "name": "string",
      "active": true
    }
  ],
  "stripe_publishable_key": "string"
}

MCP

Model Context Protocol JSON-RPC endpoint and discovery

Discover the MCP server

GET
https://api.nitrosend.com/mcp

Returns MCP discovery metadata for clients that connect over HTTP.

Response

200OKobject

MCP discovery metadata

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Discover the MCP server
curl -X GET 'https://api.nitrosend.com/mcp'
const response = await fetch('https://api.nitrosend.com/mcp', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/mcp')
data = response.json()
200
{}

MCP JSON-RPC endpoint

POST
https://api.nitrosend.com/mcp

JSON-RPC endpoint for the Nitrosend MCP server. Read nitro://account, nitro://config, or call nitro_get_status for bound account identity; there is no per-tool account override argument.

Body

application/json
object

Response

200OKobject

JSON-RPC response

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

MCP JSON-RPC endpoint
curl -X POST 'https://api.nitrosend.com/mcp' \
  -H 'Content-Type: application/json' \
  -d '{}'
const response = await fetch('https://api.nitrosend.com/mcp', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({}),
});

const data = await response.json();
import requests

payload = {}

response = requests.post('https://api.nitrosend.com/mcp', json=payload)
data = response.json()
Request Body
{}
200
{}

Chat Sessions

List persisted chat sessions

GET
https://api.nitrosend.com/v1/my/chat_sessions

Parameters

pageinteger1query
limitinteger<= 10025query

Response

200OKArray<ChatSession>

Paginated list of chat sessions

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

List persisted chat sessions
curl -X GET 'https://api.nitrosend.com/v1/my/chat_sessions'
const response = await fetch('https://api.nitrosend.com/v1/my/chat_sessions', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/chat_sessions')
data = response.json()
200
[
  {
    "id": 0,
    "account_id": 0,
    "brand_id": 0,
    "title": "string",
    "status": "active",
    "started_at": "2024-01-15T09:30:00Z",
    "last_message_at": "2024-01-15T09:30:00Z",
    "message_count": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  }
]

Get a persisted chat session with ordered messages

GET
https://api.nitrosend.com/v1/my/chat_sessions/{id}

Parameters

idintegerrequiredpath

Response

200OKChatSession & object

Chat session

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Get a persisted chat session with ordered messages
curl -X GET 'https://api.nitrosend.com/v1/my/chat_sessions/{id}'
const response = await fetch('https://api.nitrosend.com/v1/my/chat_sessions/{id}', {
  method: 'GET',
});

const data = await response.json();
import requests

response = requests.get('https://api.nitrosend.com/v1/my/chat_sessions/{id}')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "title": "string",
  "status": "active",
  "started_at": "2024-01-15T09:30:00Z",
  "last_message_at": "2024-01-15T09:30:00Z",
  "message_count": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "messages": [
    {
      "id": 0,
      "role": "user",
      "content": "string",
      "tool_calls": [
        {}
      ],
      "sequence": 0,
      "created_at": "2024-01-15T09:30:00Z"
    }
  ]
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Close an active persisted chat session

PATCH
https://api.nitrosend.com/v1/my/chat_sessions/{id}/close

Parameters

idintegerrequiredpath

Response

200OKChatSession

Closed chat session

404Not FoundError

Resource not found

Authorization

BearerAuthhttp (bearer)

Authentication scheme depends on the route:

  • Authenticated endpoints (/v1/my/*) accept a server API key (nskey_live_...) or a JWT token. API key is checked first; if not found, falls back to Devise JWT authentication.
  • Public website-form endpoints (/v1/public/*) require a brand public key (wpkey_live_...). Secret keys (nskey_live_...) are rejected here so they can't be smuggled into browser code.

For accounts with multiple brands, include the X-Brand-SID header to scope requests to a specific brand. If omitted, the account's default brand is used. JWT requests can also include X-Account-ID to select an accessible account.

Close an active persisted chat session
curl -X PATCH 'https://api.nitrosend.com/v1/my/chat_sessions/{id}/close'
const response = await fetch('https://api.nitrosend.com/v1/my/chat_sessions/{id}/close', {
  method: 'PATCH',
});

const data = await response.json();
import requests

response = requests.patch('https://api.nitrosend.com/v1/my/chat_sessions/{id}/close')
data = response.json()
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "title": "string",
  "status": "active",
  "started_at": "2024-01-15T09:30:00Z",
  "last_message_at": "2024-01-15T09:30:00Z",
  "message_count": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

Models

TimelineEntry

object

One normalised, whitelisted entry in a contact's unified timeline.

idstringrequired

Stable composite id, e.g. "event-123".

kindstringeventactivitylifecyclerequired
typestringrequired

Event/activity name, e.g. checkout, opened.

titlestringrequired

Human-readable label for the entry.

occurred_atstring<date-time>required
metaobjectrequired

Display-only extras (e.g. amount, resource_name). No internal fields.

Example
{
  "id": "string",
  "kind": "event",
  "type": "string",
  "title": "string",
  "occurred_at": "2024-01-15T09:30:00Z",
  "meta": {}
}

Error

object
codeintegerrequired
messagestringrequired
errorbooleanrequired
error_codestring | null

Optional machine-readable error reason.

Example
{
  "code": 0,
  "message": "string",
  "error": true,
  "error_code": "string"
}

User

object
idinteger
first_namestring | null
last_namestring | null
emailstring<email>
mobilestring | null
country_codestring | null
time_zonestring | null
adminboolean
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "first_name": "string",
  "last_name": "string",
  "email": "user@example.com",
  "mobile": "string",
  "country_code": "string",
  "time_zone": "string",
  "admin": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Impersonator

object
idinteger
emailstring<email>
namestring | null
Example
{
  "id": 0,
  "email": "user@example.com",
  "name": "string"
}

ImpersonationStatus

object
impersonatingbooleanrequired
impersonatorImpersonator | nullrequired
Example
{
  "impersonating": true,
  "impersonator": {
    "id": 0,
    "email": "user@example.com",
    "name": "string"
  }
}

ImpersonationExit

object
redirect_urlstring<uri>required
Example
{
  "redirect_url": "https://api.nitrosend.com/adm"
}

Account

object
idinteger
namestring | null
avatarstring | null

Signed blob ID

bannerstring | null

Signed blob ID

account_tierstringfreepaidtrusted
safe_mode_enabledboolean
billingobject
Show child attributes
plan_namestring
overageobject
resourcesobject
Show child attributes
emailobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
smsobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
aiobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
brandsobject

Per-account brand-count headroom. Usage for email, SMS, and AI remains pooled at the account level; only brand count is capped here. A limit of 0 with unlimited=true means unlimited brands.

Show child attributes
usedinteger
limitinteger

Raw plan max_brands value. 0 means unlimited when unlimited is true.

remaininginteger | null

Null when unlimited is true.

unlimitedboolean
can_createboolean

True when plan headroom allows creating another brand.

lifetimeobject
Show child attributes
email_sentinteger
sms_sentinteger
ai_usedinteger
brandsArray<Brand>
Show child attributes
idinteger
sidstringread only

Public secure identifier (non-sequential)

account_idinteger
brand_colorstring | null
text_colorstring | null
bg_colorstring | null
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring | null
font_bodystring | null
brand_documentstring | null
style_notesstring | null
tonestring | null
company_descriptionstring | null
industrystring | null
example_copyArray<string> | null
default_headerobject | null
default_footerobject | null
default_themeobject | null
physical_addressstring | null
company_namestring | null
source_urlstring | null
last_scraped_atstring<date-time> | null
linksArray<object> | null
Show child attributes
urlstring
iconstring
titlestring
logostring | null

Logo URL

completeboolean

True when brand_color and company_name are set

email_from_namestring | null
email_from_emailstring | null
email_reply_tostring | null
email_view_onlineboolean

Default-off brand setting that injects a campaign view-in-browser link when a verified tracking domain is available.

test_email_recipientsArray<string>
onboarding_stateobject

JSONB — keys are step names, values are completion metadata

onboardingobject
Show child attributes
stepsobject

Current state of each onboarding step

progressobject
Show child attributes
completedinteger
totalinteger
domain_verifiedboolean
can_sendboolean
byo_routingobject

Warns when the brand has connected its own (BYO) email provider but verified sending domains still route through nitrosend's shared managed pool. Read-only; never blocks a send. mismatch is false (and message null) when all is well.

Show child attributes
mismatchboolean
providerstring | null

The connected BYO provider, e.g. ses.

bypassing_domainsArray<string>

Verified domains still sending through the shared managed pool.

messagestring | null
subscribed_contacts_countinteger

Count of subscribed contacts in this brand.

using_sandboxboolean
sandbox_emailstring | null
sandbox_monthly_capinteger
sandbox_sends_remaininginteger
capabilitiesobject
created_atstring<date-time>
updated_atstring<date-time>
teamobject
Show child attributes
seat_limitinteger
seat_countinteger
member_countinteger
invite_countinteger
current_rolestring | null
can_manage_teamboolean
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "name": "string",
  "avatar": "string",
  "banner": "string",
  "account_tier": "free",
  "safe_mode_enabled": true,
  "billing": {
    "plan_name": "string",
    "overage": {},
    "resources": {
      "email": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "sms": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      },
      "ai": {
        "used": 0,
        "allowance": 0,
        "remaining": 0,
        "overage_rate": 0
      }
    },
    "brands": {
      "used": 0,
      "limit": 0,
      "remaining": 0,
      "unlimited": true,
      "can_create": true
    },
    "lifetime": {
      "email_sent": 0,
      "sms_sent": 0,
      "ai_used": 0
    }
  },
  "brands": [
    {
      "id": 0,
      "sid": "string",
      "account_id": 0,
      "brand_color": "string",
      "text_color": "string",
      "bg_color": "string",
      "radius": 0,
      "spacing_density": "compact",
      "font_heading": "string",
      "font_body": "string",
      "brand_document": "string",
      "style_notes": "string",
      "tone": "string",
      "company_description": "string",
      "industry": "string",
      "example_copy": [
        "string"
      ],
      "default_header": {},
      "default_footer": {},
      "default_theme": {},
      "physical_address": "string",
      "company_name": "string",
      "source_url": "string",
      "last_scraped_at": "2024-01-15T09:30:00Z",
      "links": [
        {
          "url": "string",
          "icon": "string",
          "title": "string"
        }
      ],
      "logo": "string",
      "complete": true,
      "email_from_name": "string",
      "email_from_email": "string",
      "email_reply_to": "string",
      "email_view_online": true,
      "test_email_recipients": [
        "user@example.com"
      ],
      "onboarding_state": {},
      "onboarding": {
        "steps": {},
        "progress": {
          "completed": 0,
          "total": 0
        }
      },
      "domain_verified": true,
      "can_send": true,
      "byo_routing": {
        "mismatch": true,
        "provider": "string",
        "bypassing_domains": [
          "string"
        ],
        "message": "string"
      },
      "subscribed_contacts_count": 0,
      "using_sandbox": true,
      "sandbox_email": "string",
      "sandbox_monthly_cap": 0,
      "sandbox_sends_remaining": 0,
      "capabilities": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "team": {
    "seat_limit": 0,
    "seat_count": 0,
    "member_count": 0,
    "invite_count": 0,
    "current_role": "string",
    "can_manage_team": true
  },
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

AccountTeam

object
accountAccount
Show child attributes
idinteger
namestring | null
avatarstring | null

Signed blob ID

bannerstring | null

Signed blob ID

account_tierstringfreepaidtrusted
safe_mode_enabledboolean
billingobject
Show child attributes
plan_namestring
overageobject
resourcesobject
Show child attributes
emailobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
smsobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
aiobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
brandsobject

Per-account brand-count headroom. Usage for email, SMS, and AI remains pooled at the account level; only brand count is capped here. A limit of 0 with unlimited=true means unlimited brands.

Show child attributes
usedinteger
limitinteger

Raw plan max_brands value. 0 means unlimited when unlimited is true.

remaininginteger | null

Null when unlimited is true.

unlimitedboolean
can_createboolean

True when plan headroom allows creating another brand.

lifetimeobject
Show child attributes
email_sentinteger
sms_sentinteger
ai_usedinteger
brandsArray<Brand>
Show child attributes
idinteger
sidstringread only

Public secure identifier (non-sequential)

account_idinteger
brand_colorstring | null
text_colorstring | null
bg_colorstring | null
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring | null
font_bodystring | null
brand_documentstring | null
style_notesstring | null
tonestring | null
company_descriptionstring | null
industrystring | null
example_copyArray<string> | null
default_headerobject | null
default_footerobject | null
default_themeobject | null
physical_addressstring | null
company_namestring | null
source_urlstring | null
last_scraped_atstring<date-time> | null
linksArray<object> | null
Show child attributes
urlstring
iconstring
titlestring
logostring | null

Logo URL

completeboolean

True when brand_color and company_name are set

email_from_namestring | null
email_from_emailstring | null
email_reply_tostring | null
email_view_onlineboolean

Default-off brand setting that injects a campaign view-in-browser link when a verified tracking domain is available.

test_email_recipientsArray<string>
onboarding_stateobject

JSONB — keys are step names, values are completion metadata

onboardingobject
Show child attributes
stepsobject

Current state of each onboarding step

progressobject
Show child attributes
completedinteger
totalinteger
domain_verifiedboolean
can_sendboolean
byo_routingobject

Warns when the brand has connected its own (BYO) email provider but verified sending domains still route through nitrosend's shared managed pool. Read-only; never blocks a send. mismatch is false (and message null) when all is well.

Show child attributes
mismatchboolean
providerstring | null

The connected BYO provider, e.g. ses.

bypassing_domainsArray<string>

Verified domains still sending through the shared managed pool.

messagestring | null
subscribed_contacts_countinteger

Count of subscribed contacts in this brand.

using_sandboxboolean
sandbox_emailstring | null
sandbox_monthly_capinteger
sandbox_sends_remaininginteger
capabilitiesobject
created_atstring<date-time>
updated_atstring<date-time>
teamobject
Show child attributes
seat_limitinteger
seat_countinteger
member_countinteger
invite_countinteger
current_rolestring | null
can_manage_teamboolean
created_atstring<date-time>
updated_atstring<date-time>
membershipsArray<AccountMembership>
Show child attributes
idinteger
rolestringmemberadminowner
user_idinteger
emailstring<email>
namestring | null
created_atstring<date-time>
updated_atstring<date-time>
invitesArray<AccountInvite>
Show child attributes
idinteger
account_idinteger
emailstring<email>
rolestringmemberadmin
tokenstring
statusstringpendingacceptedrevokedexpired
accepted_atstring<date-time> | null
revoked_atstring<date-time> | null
expires_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
accessible_accountsArray<Account>
Show child attributes
idinteger
namestring | null
avatarstring | null

Signed blob ID

bannerstring | null

Signed blob ID

account_tierstringfreepaidtrusted
safe_mode_enabledboolean
billingobject
Show child attributes
plan_namestring
overageobject
resourcesobject
Show child attributes
emailobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
smsobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
aiobject
Show child attributes
usedinteger
allowanceinteger
remaininginteger
overage_ratenumber
brandsobject

Per-account brand-count headroom. Usage for email, SMS, and AI remains pooled at the account level; only brand count is capped here. A limit of 0 with unlimited=true means unlimited brands.

Show child attributes
usedinteger
limitinteger

Raw plan max_brands value. 0 means unlimited when unlimited is true.

remaininginteger | null

Null when unlimited is true.

unlimitedboolean
can_createboolean

True when plan headroom allows creating another brand.

lifetimeobject
Show child attributes
email_sentinteger
sms_sentinteger
ai_usedinteger
brandsArray<Brand>
Show child attributes
idinteger
sidstringread only

Public secure identifier (non-sequential)

account_idinteger
brand_colorstring | null
text_colorstring | null
bg_colorstring | null
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring | null
font_bodystring | null
brand_documentstring | null
style_notesstring | null
tonestring | null
company_descriptionstring | null
industrystring | null
example_copyArray<string> | null
default_headerobject | null
default_footerobject | null
default_themeobject | null
physical_addressstring | null
company_namestring | null
source_urlstring | null
last_scraped_atstring<date-time> | null
linksArray<object> | null
Show child attributes
urlstring
iconstring
titlestring
logostring | null

Logo URL

completeboolean

True when brand_color and company_name are set

email_from_namestring | null
email_from_emailstring | null
email_reply_tostring | null
email_view_onlineboolean

Default-off brand setting that injects a campaign view-in-browser link when a verified tracking domain is available.

test_email_recipientsArray<string>
onboarding_stateobject

JSONB — keys are step names, values are completion metadata

onboardingobject
Show child attributes
stepsobject

Current state of each onboarding step

progressobject
Show child attributes
completedinteger
totalinteger
domain_verifiedboolean
can_sendboolean
byo_routingobject

Warns when the brand has connected its own (BYO) email provider but verified sending domains still route through nitrosend's shared managed pool. Read-only; never blocks a send. mismatch is false (and message null) when all is well.

Show child attributes
mismatchboolean
providerstring | null

The connected BYO provider, e.g. ses.

bypassing_domainsArray<string>

Verified domains still sending through the shared managed pool.

messagestring | null
subscribed_contacts_countinteger

Count of subscribed contacts in this brand.

using_sandboxboolean
sandbox_emailstring | null
sandbox_monthly_capinteger
sandbox_sends_remaininginteger
capabilitiesobject
created_atstring<date-time>
updated_atstring<date-time>
teamobject
Show child attributes
seat_limitinteger
seat_countinteger
member_countinteger
invite_countinteger
current_rolestring | null
can_manage_teamboolean
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "account": {
    "id": 0,
    "name": "string",
    "avatar": "string",
    "banner": "string",
    "account_tier": "free",
    "safe_mode_enabled": true,
    "billing": {
      "plan_name": "string",
      "overage": {},
      "resources": {
        "email": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "sms": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        },
        "ai": {
          "used": 0,
          "allowance": 0,
          "remaining": 0,
          "overage_rate": 0
        }
      },
      "brands": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "unlimited": true,
        "can_create": true
      },
      "lifetime": {
        "email_sent": 0,
        "sms_sent": 0,
        "ai_used": 0
      }
    },
    "brands": [
      {
        "id": 0,
        "sid": "string",
        "account_id": 0,
        "brand_color": "string",
        "text_color": "string",
        "bg_color": "string",
        "radius": 0,
        "spacing_density": "compact",
        "font_heading": "string",
        "font_body": "string",
        "brand_document": "string",
        "style_notes": "string",
        "tone": "string",
        "company_description": "string",
        "industry": "string",
        "example_copy": [
          "string"
        ],
        "default_header": {},
        "default_footer": {},
        "default_theme": {},
        "physical_address": "string",
        "company_name": "string",
        "source_url": "string",
        "last_scraped_at": "2024-01-15T09:30:00Z",
        "links": [
          {
            "url": "string",
            "icon": "string",
            "title": "string"
          }
        ],
        "logo": "string",
        "complete": true,
        "email_from_name": "string",
        "email_from_email": "string",
        "email_reply_to": "string",
        "email_view_online": true,
        "test_email_recipients": [
          "user@example.com"
        ],
        "onboarding_state": {},
        "onboarding": {
          "steps": {},
          "progress": {
            "completed": 0,
            "total": 0
          }
        },
        "domain_verified": true,
        "can_send": true,
        "byo_routing": {
          "mismatch": true,
          "provider": "string",
          "bypassing_domains": [
            "string"
          ],
          "message": "string"
        },
        "subscribed_contacts_count": 0,
        "using_sandbox": true,
        "sandbox_email": "string",
        "sandbox_monthly_cap": 0,
        "sandbox_sends_remaining": 0,
        "capabilities": {},
        "created_at": "2024-01-15T09:30:00Z",
        "updated_at": "2024-01-15T09:30:00Z"
      }
    ],
    "team": {
      "seat_limit": 0,
      "seat_count": 0,
      "member_count": 0,
      "invite_count": 0,
      "current_role": "string",
      "can_manage_team": true
    },
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "memberships": [
    {
      "id": 0,
      "role": "member",
      "user_id": 0,
      "email": "user@example.com",
      "name": "string",
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "invites": [
    {
      "id": 0,
      "account_id": 0,
      "email": "user@example.com",
      "role": "member",
      "token": "string",
      "status": "pending",
      "accepted_at": "2024-01-15T09:30:00Z",
      "revoked_at": "2024-01-15T09:30:00Z",
      "expires_at": "2024-01-15T09:30:00Z",
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ],
  "accessible_accounts": [
    {
      "id": 0,
      "name": "string",
      "avatar": "string",
      "banner": "string",
      "account_tier": "free",
      "safe_mode_enabled": true,
      "billing": {
        "plan_name": "string",
        "overage": {},
        "resources": {
          "email": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          },
          "sms": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          },
          "ai": {
            "used": 0,
            "allowance": 0,
            "remaining": 0,
            "overage_rate": 0
          }
        },
        "brands": {
          "used": 0,
          "limit": 0,
          "remaining": 0,
          "unlimited": true,
          "can_create": true
        },
        "lifetime": {
          "email_sent": 0,
          "sms_sent": 0,
          "ai_used": 0
        }
      },
      "brands": [
        {
          "id": 0,
          "sid": "string",
          "account_id": 0,
          "brand_color": "string",
          "text_color": "string",
          "bg_color": "string",
          "radius": 0,
          "spacing_density": "compact",
          "font_heading": "string",
          "font_body": "string",
          "brand_document": "string",
          "style_notes": "string",
          "tone": "string",
          "company_description": "string",
          "industry": "string",
          "example_copy": [
            "string"
          ],
          "default_header": {},
          "default_footer": {},
          "default_theme": {},
          "physical_address": "string",
          "company_name": "string",
          "source_url": "string",
          "last_scraped_at": "2024-01-15T09:30:00Z",
          "links": [
            {
              "url": "string",
              "icon": "string",
              "title": "string"
            }
          ],
          "logo": "string",
          "complete": true,
          "email_from_name": "string",
          "email_from_email": "string",
          "email_reply_to": "string",
          "email_view_online": true,
          "test_email_recipients": [
            "user@example.com"
          ],
          "onboarding_state": {},
          "onboarding": {
            "steps": {},
            "progress": {
              "completed": 0,
              "total": 0
            }
          },
          "domain_verified": true,
          "can_send": true,
          "byo_routing": {
            "mismatch": true,
            "provider": "string",
            "bypassing_domains": [
              "string"
            ],
            "message": "string"
          },
          "subscribed_contacts_count": 0,
          "using_sandbox": true,
          "sandbox_email": "string",
          "sandbox_monthly_cap": 0,
          "sandbox_sends_remaining": 0,
          "capabilities": {},
          "created_at": "2024-01-15T09:30:00Z",
          "updated_at": "2024-01-15T09:30:00Z"
        }
      ],
      "team": {
        "seat_limit": 0,
        "seat_count": 0,
        "member_count": 0,
        "invite_count": 0,
        "current_role": "string",
        "can_manage_team": true
      },
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

AccountMembership

object
idinteger
rolestringmemberadminowner
user_idinteger
emailstring<email>
namestring | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "role": "member",
  "user_id": 0,
  "email": "user@example.com",
  "name": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

AccountInvite

object
idinteger
account_idinteger
emailstring<email>
rolestringmemberadmin
tokenstring
statusstringpendingacceptedrevokedexpired
accepted_atstring<date-time> | null
revoked_atstring<date-time> | null
expires_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "email": "user@example.com",
  "role": "member",
  "token": "string",
  "status": "pending",
  "accepted_at": "2024-01-15T09:30:00Z",
  "revoked_at": "2024-01-15T09:30:00Z",
  "expires_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Subscription

object
idinteger
plan_idinteger
plan_namestring | null
statusstringpendingactiveinactivecanceledfree_tier
intervalstringmonthyearweekday
currencystring
subtotal_centsinteger
tax_centsinteger
total_centsinteger
base_price_centsinteger
discount_centsinteger

Per-period Stripe discount in cents (0 when none).

next_payment_centsinteger

Amount billed next period

discount_end_atstring<date-time> | null

When the discount stops (null for forever/once or no discount).

activatedboolean
Example
{
  "id": 0,
  "plan_id": 0,
  "plan_name": "string",
  "status": "pending",
  "interval": "month",
  "currency": "string",
  "subtotal_cents": 0,
  "tax_cents": 0,
  "total_cents": 0,
  "base_price_cents": 0,
  "discount_cents": 0,
  "next_payment_cents": 0,
  "discount_end_at": "2024-01-15T09:30:00Z",
  "activated": true
}

SubscriptionCreateRequest

object
plan_idintegerrequired
stripe_tokenstring | null

Stripe card token for paid-plan creation.

coupon_codestring | null

Customer-entered Stripe promotion code or coupon ID.

Example
{
  "plan_id": 0,
  "stripe_token": "string",
  "coupon_code": "string"
}

SubscriptionChangeRequest

object
plan_idintegerrequired
coupon_codestring | null

Customer-entered Stripe promotion code or coupon ID.

Example
{
  "plan_id": 0,
  "coupon_code": "string"
}

SubscriptionCouponPreviewRequest

object
plan_idintegerrequired
coupon_codestringrequired

Customer-entered Stripe promotion code or coupon ID.

Example
{
  "plan_id": 0,
  "coupon_code": "string"
}

SubscriptionCouponPreview

object
codestringrequired
discount_labelstringrequired
discount_centsintegerrequired
subtotal_centsintegerrequired
tax_centsintegerrequired
total_centsintegerrequired
total_after_discount_centsintegerrequired
currencystringrequired
Example
{
  "code": "string",
  "discount_label": "string",
  "discount_cents": 0,
  "subtotal_cents": 0,
  "tax_cents": 0,
  "total_cents": 0,
  "total_after_discount_cents": 0,
  "currency": "string"
}

OAuthPopupAccount

object
idintegerrequired
namestringrequired
can_manageboolean
needs_subscribebooleanrequired
Example
{
  "id": 0,
  "name": "string",
  "can_manage": true,
  "needs_subscribe": true
}

OAuthPopupPlan

object
idintegerrequired
namestringrequired
slugstringrequired
base_price_centsintegerrequired
intervalstring | null
Example
{
  "id": 0,
  "name": "string",
  "slug": "string",
  "base_price_cents": 0,
  "interval": "string"
}

OAuthPopupContext

object
accountsArray<OAuthPopupAccount>required
Show child attributes
idintegerrequired
namestringrequired
can_manageboolean
needs_subscribebooleanrequired
plansArray<OAuthPopupPlan>required
Show child attributes
idintegerrequired
namestringrequired
slugstringrequired
base_price_centsintegerrequired
intervalstring | null
stripe_publishable_keystring | null
Example
{
  "accounts": [
    {
      "id": 0,
      "name": "string",
      "can_manage": true,
      "needs_subscribe": true
    }
  ],
  "plans": [
    {
      "id": 0,
      "name": "string",
      "slug": "string",
      "base_price_cents": 0,
      "interval": "string"
    }
  ],
  "stripe_publishable_key": "string"
}

OAuthPopupSubscribeRequest

object
plan_idintegerrequired
account_idinteger | null
stripe_tokenstring | null
Example
{
  "plan_id": 0,
  "account_id": 0,
  "stripe_token": "string"
}

OAuthPopupSubscribeResponse

object
next_stepstringconsentrequired
Example
{
  "next_step": "consent"
}

OAuthLaunchRequest

object
providerstringgoogle_oauth2githubrequired
auth_intentstringappagentapp
auth_stepstringloginsignuplogin
resume_urlstring<uri> | null

Required when auth_intent=agent; must point to the frontend /oauth/connect route.

Example
{
  "provider": "google_oauth2",
  "auth_intent": "app",
  "auth_step": "login",
  "resume_url": "https://example.com"
}

OAuthLaunchResponse

object
launch_urlstring<uri>required
Example
{
  "launch_url": "https://example.com"
}

Brand

object
idinteger
sidstringread only

Public secure identifier (non-sequential)

account_idinteger
brand_colorstring | null
text_colorstring | null
bg_colorstring | null
radiusinteger | null[0, 64]
spacing_densitystring | nullcompactnormalspacious
font_headingstring | null
font_bodystring | null
brand_documentstring | null
style_notesstring | null
tonestring | null
company_descriptionstring | null
industrystring | null
example_copyArray<string> | null
default_headerobject | null
default_footerobject | null
default_themeobject | null
physical_addressstring | null
company_namestring | null
source_urlstring | null
last_scraped_atstring<date-time> | null
linksArray<object> | null
Show child attributes
urlstring
iconstring
titlestring
logostring | null

Logo URL

completeboolean

True when brand_color and company_name are set

email_from_namestring | null
email_from_emailstring | null
email_reply_tostring | null
email_view_onlineboolean

Default-off brand setting that injects a campaign view-in-browser link when a verified tracking domain is available.

test_email_recipientsArray<string>
onboarding_stateobject

JSONB — keys are step names, values are completion metadata

onboardingobject
Show child attributes
stepsobject

Current state of each onboarding step

progressobject
Show child attributes
completedinteger
totalinteger
domain_verifiedboolean
can_sendboolean
byo_routingobject

Warns when the brand has connected its own (BYO) email provider but verified sending domains still route through nitrosend's shared managed pool. Read-only; never blocks a send. mismatch is false (and message null) when all is well.

Show child attributes
mismatchboolean
providerstring | null

The connected BYO provider, e.g. ses.

bypassing_domainsArray<string>

Verified domains still sending through the shared managed pool.

messagestring | null
subscribed_contacts_countinteger

Count of subscribed contacts in this brand.

using_sandboxboolean
sandbox_emailstring | null
sandbox_monthly_capinteger
sandbox_sends_remaininginteger
capabilitiesobject
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "sid": "string",
  "account_id": 0,
  "brand_color": "string",
  "text_color": "string",
  "bg_color": "string",
  "radius": 0,
  "spacing_density": "compact",
  "font_heading": "string",
  "font_body": "string",
  "brand_document": "string",
  "style_notes": "string",
  "tone": "string",
  "company_description": "string",
  "industry": "string",
  "example_copy": [
    "string"
  ],
  "default_header": {},
  "default_footer": {},
  "default_theme": {},
  "physical_address": "string",
  "company_name": "string",
  "source_url": "string",
  "last_scraped_at": "2024-01-15T09:30:00Z",
  "links": [
    {
      "url": "string",
      "icon": "string",
      "title": "string"
    }
  ],
  "logo": "string",
  "complete": true,
  "email_from_name": "string",
  "email_from_email": "string",
  "email_reply_to": "string",
  "email_view_online": true,
  "test_email_recipients": [
    "user@example.com"
  ],
  "onboarding_state": {},
  "onboarding": {
    "steps": {},
    "progress": {
      "completed": 0,
      "total": 0
    }
  },
  "domain_verified": true,
  "can_send": true,
  "byo_routing": {
    "mismatch": true,
    "provider": "string",
    "bypassing_domains": [
      "string"
    ],
    "message": "string"
  },
  "subscribed_contacts_count": 0,
  "using_sandbox": true,
  "sandbox_email": "string",
  "sandbox_monthly_cap": 0,
  "sandbox_sends_remaining": 0,
  "capabilities": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

BrandDeletionSafetyImpact

object
contactsinteger>= 0required
campaignsinteger>= 0required
flowsinteger>= 0required
templatesinteger>= 0required
domainsinteger>= 0required
messagesinteger>= 0required
Example
{
  "contacts": 0,
  "campaigns": 0,
  "flows": 0,
  "templates": 0,
  "domains": 0,
  "messages": 0
}

BrandDeletionSafetyActiveSends

object
queued_messagesinteger>= 0required
active_campaignsinteger>= 0required

Scheduled or live campaigns.

Example
{
  "queued_messages": 0,
  "active_campaigns": 0
}

BrandDeletionSafety

object
deletion_impactBrandDeletionSafetyImpactrequired
Show child attributes
contactsinteger>= 0required
campaignsinteger>= 0required
flowsinteger>= 0required
templatesinteger>= 0required
domainsinteger>= 0required
messagesinteger>= 0required
active_sendsBrandDeletionSafetyActiveSendsrequired
Show child attributes
queued_messagesinteger>= 0required
active_campaignsinteger>= 0required

Scheduled or live campaigns.

can_deletebooleanrequired

True when no force confirmation is required.

requires_forcebooleanrequired

True when active sends require force=true to delete.

Example
{
  "deletion_impact": {
    "contacts": 0,
    "campaigns": 0,
    "flows": 0,
    "templates": 0,
    "domains": 0,
    "messages": 0
  },
  "active_sends": {
    "queued_messages": 0,
    "active_campaigns": 0
  },
  "can_delete": true,
  "requires_force": true
}

SetupCenterPayload

object
cardsArray<SetupCenterCard>
Show child attributes
idstringbrand_kit_scandnsplanconnect_agentimport_subscribersconnect_transactional
completeboolean
acknowledgedboolean
acknowledged_atstring<date-time> | null
sectionsobject
Show child attributes
requiredArray<string>
recommendedArray<string>
progressobject
Show child attributes
completedinteger
totalinteger
seenboolean
dismissedboolean
completeboolean
Example
{
  "cards": [
    {
      "id": "brand_kit_scan",
      "complete": true,
      "acknowledged": true,
      "acknowledged_at": "2024-01-15T09:30:00Z"
    }
  ],
  "sections": {
    "required": [
      "string"
    ],
    "recommended": [
      "string"
    ]
  },
  "progress": {
    "completed": 0,
    "total": 0
  },
  "seen": true,
  "dismissed": true,
  "complete": true
}

SetupCenterCard

object
idstringbrand_kit_scandnsplanconnect_agentimport_subscribersconnect_transactional
completeboolean
acknowledgedboolean
acknowledged_atstring<date-time> | null
Example
{
  "id": "brand_kit_scan",
  "complete": true,
  "acknowledged": true,
  "acknowledged_at": "2024-01-15T09:30:00Z"
}

FieldCatalog

object
idintegerrequired
keystringrequired

Dot-separated field key. Typed contact columns use the bare column name (e.g. email, first_name). JSONB data keys are prefixed with data. (e.g. data.plan, data.apollo.title, data.hubspot.company, data.tags).

categorystringcontactcustomenrichmentengagementtagrequired

Derived from the key namespace.

  • contact — typed column on the contacts table
  • tagdata.tags array
  • enrichment — reserved enrichment namespaces such as data.apollo.*, data.pdl.*, data.attio.*, data.hubspot.*, data.stripe.*, data.shopify.*, and derived data.nitro.*
  • custom — any other data.* key
  • engagement — future engagement traits (reserved)
field_typestringstringnumberbooleandaterequired

Inferred from the first observed value; pinned and never flipped.

labelstringrequired

Human-readable label. Defaults to a humanised version of the key.

promotedbooleanrequired

Whether this field is pinned as a default column in the contacts grid.

fill_ratestring | null

Percentage of contacts that have a non-null value for this field. Computed asynchronously; may be null if the refresh job has not run yet.

fill_rate_refreshed_atstring<date-time> | null

When fill_rate was last computed.

created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "key": "string",
  "category": "contact",
  "field_type": "string",
  "label": "string",
  "promoted": true,
  "fill_rate": "75.0",
  "fill_rate_refreshed_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Contact

object
idinteger
brand_idinteger | null
uuidstring<uuid>
first_namestring | null
last_namestring | null
sourcestring | null
country_codestring | null
flag_emojistring | null

Unicode regional-indicator emoji pair derived from country_code (e.g. "🇦🇺"). Null when country_code is blank.

dataobject

Custom key-value data. The reserved key tags holds an array of string labels used for segmentation and targeting. Reserved enrichment namespaces such as apollo, pdl, attio, hubspot, stripe, shopify, and derived nitro may appear when integrations or enrichment jobs write source-scoped data.

subscribed_phoneboolean
subscribed_emailboolean
emailstring<email> | null

Convenience value for the preferred email channel. Full channel detail remains in channels[].

subscribedobject

Denormalized subscription summary by channel family.

Show child attributes
emailboolean
phoneboolean
verification_statusstringverifiedsuppressedunverified
enrichment_statusstringenrichednot_enriched
mailbox_providerstringgmailappleoutlookcorporateunknown

Derived mailbox provider for the contact's primary email domain. Known consumer domains map to gmail, apple, or outlook; any other valid email domain maps to corporate; unknown means no email is present.

list_idsArray<integer>
last_interacted_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
engagementobject | null

Aggregated email engagement rollup for this contact. All fields are nil-safe: when no rollup row exists yet, rating is "never", counts are 0, rates and timestamps are null.

Show child attributes
ratingstringengagedwarmcoolingdormantnever

Five-tier engagement rating based on recency of opens and clicks.

emails_sentinteger
unique_opensinteger
clicksinteger
open_ratenumber<float> | null
click_ratenumber<float> | null
last_opened_atstring<date-time> | null
last_clicked_atstring<date-time> | null
channelsArray<ContactChannel>
Show child attributes
idinteger
contact_idinteger
kindstringemailphone
valuestring
subscribedboolean
verifiedboolean
opt_in_atstring<date-time> | null
opt_out_atstring<date-time> | null
sent_countinteger
fail_countinteger
dataobject
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "brand_id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "first_name": "string",
  "last_name": "string",
  "source": "string",
  "country_code": "string",
  "flag_emoji": "string",
  "data": {
    "tags": [
      "vip",
      "newsletter"
    ],
    "plan": "pro"
  },
  "subscribed_phone": true,
  "subscribed_email": true,
  "email": "user@example.com",
  "subscribed": {
    "email": true,
    "phone": true
  },
  "verification_status": "verified",
  "enrichment_status": "enriched",
  "mailbox_provider": "gmail",
  "list_ids": [
    0
  ],
  "last_interacted_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "engagement": {
    "rating": "engaged",
    "emails_sent": 0,
    "unique_opens": 0,
    "clicks": 0,
    "open_rate": 0,
    "click_rate": 0,
    "last_opened_at": "2024-01-15T09:30:00Z",
    "last_clicked_at": "2024-01-15T09:30:00Z"
  },
  "channels": [
    {
      "id": 0,
      "contact_id": 0,
      "kind": "email",
      "value": "string",
      "subscribed": true,
      "verified": true,
      "opt_in_at": "2024-01-15T09:30:00Z",
      "opt_out_at": "2024-01-15T09:30:00Z",
      "sent_count": 0,
      "fail_count": 0,
      "data": {},
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

ContactChannel

object
idinteger
contact_idinteger
kindstringemailphone
valuestring
subscribedboolean
verifiedboolean
opt_in_atstring<date-time> | null
opt_out_atstring<date-time> | null
sent_countinteger
fail_countinteger
dataobject
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "contact_id": 0,
  "kind": "email",
  "value": "string",
  "subscribed": true,
  "verified": true,
  "opt_in_at": "2024-01-15T09:30:00Z",
  "opt_out_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "fail_count": 0,
  "data": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

ImageAsset

object
media_kindstringimage
media_urlstring<uri>
image_urlstring<uri>
signed_idstring
filenamestring
content_typestring
byte_sizeinteger
widthinteger | null

Intrinsic pixel width when detectable.

heightinteger | null

Intrinsic pixel height when detectable.

Example
{
  "media_kind": "image",
  "media_url": "https://example.com",
  "image_url": "https://example.com",
  "signed_id": "string",
  "filename": "string",
  "content_type": "string",
  "byte_size": 0,
  "width": 0,
  "height": 0
}

ContactList

object
idinteger
account_idinteger
brand_idinteger | null
namestring
contacts_countinteger
segment_idinteger | null
staleboolean
last_populated_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "contacts_count": 0,
  "segment_id": 0,
  "stale": true,
  "last_populated_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

ListDeleteWarning

object
campaign_namesArray<string>
flow_namesArray<string>
Example
{
  "campaign_names": [
    "string"
  ],
  "flow_names": [
    "string"
  ]
}

BulkListContactsRequest

object
actionstringaddremoverequired

Add existing contacts to the list or remove them from it.

emailsArray<string>required
Example
{
  "action": "add",
  "emails": [
    "user@example.com"
  ]
}

BulkListContactsResponse

object
actionstringaddremove
list_idinteger
addedinteger

Contacts newly added for add actions

removedinteger

Contacts removed for remove actions

already_in_listArray<string>
not_in_listArray<string>
not_foundArray<string>
invalid_emailsArray<string>
Example
{
  "action": "add",
  "list_id": 0,
  "added": 0,
  "removed": 0,
  "already_in_list": [
    "user@example.com"
  ],
  "not_in_list": [
    "user@example.com"
  ],
  "not_found": [
    "user@example.com"
  ],
  "invalid_emails": [
    "string"
  ]
}

DirectUploadCreateRequest

object
purposestringimportimagemedia_asset

Set to import for CSV contact imports, or image/media_asset for image media assets.

blobobjectrequired
Show child attributes
filenamestringrequired
byte_sizeintegerrequired
checksumstringrequired

Base64-encoded MD5 checksum for Active Storage direct upload.

content_typestring
metadataobject
Example
{
  "purpose": "import",
  "blob": {
    "filename": "contacts.csv",
    "byte_size": 1048576,
    "checksum": "string",
    "content_type": "text/csv",
    "metadata": {}
  }
}

DirectUpload

object
signed_idstring

Submit this value to endpoints that consume direct uploads.

filenamestring
byte_sizeinteger
content_typestring | null
direct_uploadobject
Show child attributes
urlstring<uri>
headersobject
Example
{
  "signed_id": "string",
  "filename": "string",
  "byte_size": 0,
  "content_type": "string",
  "direct_upload": {
    "url": "https://example.com",
    "headers": {}
  }
}

ImportPolicy

object
max_file_size_bytesinteger
max_file_size_mbinteger
auto_max_rowsinteger
contact_us_max_rowsinteger
max_active_importsinteger
create_rate_limit_per_minuteinteger
direct_upload_rate_limit_per_minuteinteger
Example
{
  "max_file_size_bytes": 94371840,
  "max_file_size_mb": 90,
  "auto_max_rows": 20000,
  "contact_us_max_rows": 250000,
  "max_active_imports": 3,
  "create_rate_limit_per_minute": 10,
  "direct_upload_rate_limit_per_minute": 30
}

ImportSpec

object
resourcestringcontacts
parserstringdefault
uiobject
required_rulesobject
fieldsArray<object>
guardrailsImportPolicy
Show child attributes
max_file_size_bytesinteger
max_file_size_mbinteger
auto_max_rowsinteger
contact_us_max_rowsinteger
max_active_importsinteger
create_rate_limit_per_minuteinteger
direct_upload_rate_limit_per_minuteinteger
Example
{
  "resource": "contacts",
  "parser": "default",
  "ui": {},
  "required_rules": {},
  "fields": [
    {}
  ],
  "guardrails": {
    "max_file_size_bytes": 94371840,
    "max_file_size_mb": 90,
    "auto_max_rows": 20000,
    "contact_us_max_rows": 250000,
    "max_active_imports": 3,
    "create_rate_limit_per_minute": 10,
    "direct_upload_rate_limit_per_minute": 30
  }
}

ImportGuardrail

object
tierstringautohold_sendscontact_us

Import guardrail classification based on contact count.

statusstringokrequires_reviewcontact_sales

Client-facing guardrail status vocabulary.

contact_us_ceilinginteger

Maximum self-serve import contact count before contact-us routing.

sends_heldboolean

True when imported contacts are created but campaign sends remain held for review.

Example
{
  "tier": "auto",
  "status": "ok",
  "contact_us_ceiling": 250000,
  "sends_held": true
}

Import

object
idinteger
resourcestringcontacts
parserstringdefault
statusstringpendingprocessingfailedcanceledcompletecontact_us
total_rowsinteger | null
success_rowsinteger | null
failed_rowsinteger | null
progressobject

Canonical live-progress block (the single progress representation). pct is the only percent source; a null pct means indeterminate.

Show child attributes
statusstringpendingrunningcompletefailed
pctnumber | null
stagesArray<object>
Show child attributes
keystring
labelstring
countinteger
statestringdoneactivependingfailed
import_errorsArray<Array<integer | string>>

Row-level errors as [line_number, message, source].

columnsobject | null
optionsobject | null
assigned_list_idsArray<integer>
assigned_listsArray<object>
Show child attributes
idinteger
namestring
guardrailImportGuardrail
Show child attributes
tierstringautohold_sendscontact_us

Import guardrail classification based on contact count.

statusstringokrequires_reviewcontact_sales

Client-facing guardrail status vocabulary.

contact_us_ceilinginteger

Maximum self-serve import contact count before contact-us routing.

sends_heldboolean

True when imported contacts are created but campaign sends remain held for review.

started_atstring<date-time> | null
ended_atstring<date-time> | null
created_atstring<date-time>
Example
{
  "id": 0,
  "resource": "contacts",
  "parser": "default",
  "status": "pending",
  "total_rows": 0,
  "success_rows": 0,
  "failed_rows": 0,
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "import_errors": [
    [
      0
    ]
  ],
  "columns": {},
  "options": {},
  "assigned_list_ids": [
    0
  ],
  "assigned_lists": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "guardrail": {
    "tier": "auto",
    "status": "ok",
    "contact_us_ceiling": 250000,
    "sends_held": true
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}

Export

object
idinteger
resourcestringcontacts
formatstringcsv
statusstringpendingprocessingcompletefailed
total_rowsinteger | null
rows_writteninteger

Rows written so far; the live numerator against total_rows.

error_messagestring | null
readyboolean

True once the export is complete and the file is available.

download_pathstring | null

Path to download the CSV; present only when ready is true.

progressobject

Live job-progress block, updated as the export streams. status normalizes the job lifecycle, pct is the completion percentage (null while the row count is still unknown), and stages is the ordered Queued to Building to Ready funnel.

Show child attributes
statusstringpendingrunningcompletefailed
pctnumber | null

Completion percentage, or null when indeterminate.

stagesArray<object>
Show child attributes
keystring
labelstring
countinteger | null
statestringdoneactivependingfailed
started_atstring<date-time> | null
ended_atstring<date-time> | null
created_atstring<date-time>
Example
{
  "id": 0,
  "resource": "contacts",
  "format": "csv",
  "status": "pending",
  "total_rows": 0,
  "rows_written": 0,
  "error_message": "string",
  "ready": true,
  "download_path": "string",
  "progress": {
    "status": "pending",
    "pct": 0,
    "stages": [
      {
        "key": "string",
        "label": "string",
        "count": 0,
        "state": "done"
      }
    ]
  },
  "started_at": "2024-01-15T09:30:00Z",
  "ended_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}

SavedView

object
idinteger
surfacestringcontactssendingactivity
namestring
visibilitystringprivateshared
filtersSegmentFilterExpression
layoutSavedViewLayout | null

Table layout preset for contacts surface views. Only present when surface = contacts.

Show child attributes
columnsArray<string>

Ordered list of column keys to display

sortobject
Show child attributes
fieldstring

Column key to sort by

dirstringascdesc

Sort direction

ownerboolean

True when the current user is the creator of this view

created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "surface": "contacts",
  "name": "string",
  "visibility": "private",
  "layout": {
    "columns": [
      "string"
    ],
    "sort": {
      "field": "string",
      "dir": "asc"
    }
  },
  "owner": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

SavedViewLayout

object

Table layout preset for contacts surface views. Only present when surface = contacts.

columnsArray<string>

Ordered list of column keys to display

sortobject
Show child attributes
fieldstring

Column key to sort by

dirstringascdesc

Sort direction

Example
{
  "columns": [
    "string"
  ],
  "sort": {
    "field": "string",
    "dir": "asc"
  }
}

Segment

object
idinteger
account_idinteger
brand_idinteger | null
namestring
originstringusersystem

Who owns this segment. user (default) — created and managed by the user; system — curated by the platform (Champions, Loyal, At-Risk, Dormant, New, Suppressed, Recently unsubscribed, Bounced). System segments cannot be renamed or deleted via the API.

filtersSegmentFilterExpression
cached_countinteger | null

Last asynchronously refreshed contact count for this segment.

count_computed_atstring<date-time> | null

When cached_count was last refreshed.

created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "name": "string",
  "origin": "user",
  "cached_count": 0,
  "count_computed_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

SegmentFilterExpression

object

Request-side audience filter grammar. Use a flat array for ordinary AND filters, { "operator": "and", "children": [...] } for the legacy expression wrapper, or { "op": "and"|"or"|"not", "conditions": [...] } for nested boolean logic. NOT groups must contain exactly one condition.

One of
Array<AttributeSegmentFilter | any | any>
operatorstringandrequired
notbooleanfalsefalse

Legacy flat-AND wrapper does not carry NOT; use BooleanSegmentFilterGroup for NOT.

childrenArray<AttributeSegmentFilter | any | any>required

Segment filters are validated fail-closed. Unknown filter names, globally invalid predicates, type-incompatible predicates, bad date values, invalid enum values, and unsupported rolling-window filters return a 422 invalid_filter error instead of silently widening an audience.

Show child attributes
One of
namestringrequired

Filter name from nitro://schema: contact_first_name, contact_last_name, contact_phone_number, contact_email, contact_country, contact_subscribed_phone, contact_subscribed_email, contact_created_at, contact_last_interacted_at, contact_source, contact_tag, contact_engagement_rating, contact_emails_sent, contact_last_opened_at, contact_last_clicked_at, contact_unique_opens, contact_clicks, contact_open_rate, contact_click_rate, contact_suppressed, contact_suppression_reason, contact_bounced, contact_complained, contact_soft_bounce_count, contact_unsubscribed_at, contact_list. Catalogued custom and enrichment fields are exposed as contact_data_, with dots replaced by underscores (for example, data.attio.lifecycle_stage becomes contact_data_attio_lifecycle_stage).

predicatestringrequired

Ransack predicate: eq, not_eq, cont, not_cont, start, end, gt, lt, gteq, lteq, present, blank, true, false, in, not_in, within_days, not_within_days. Read /v1/my/flows/spec or nitro://schema for the predicates allowed by each filter type.

valueanyrequired

Filter value — string, number, boolean, array of strings for in/not_in predicates (e.g. ["engaged","warm"] for contact_engagement_rating in), or array of list ids for contact_list.

One of
predicatestringperformednot_performedrequired
predicatestringcount_at_leastcount_at_mostrequired
BooleanSegmentFilterGroup
Example
[
  {
    "name": "string",
    "predicate": "string"
  }
]

FlatAndSegmentFilterExpression

object
operatorstringandrequired
notbooleanfalsefalse

Legacy flat-AND wrapper does not carry NOT; use BooleanSegmentFilterGroup for NOT.

childrenArray<AttributeSegmentFilter | any | any>required

Segment filters are validated fail-closed. Unknown filter names, globally invalid predicates, type-incompatible predicates, bad date values, invalid enum values, and unsupported rolling-window filters return a 422 invalid_filter error instead of silently widening an audience.

Show child attributes
One of
namestringrequired

Filter name from nitro://schema: contact_first_name, contact_last_name, contact_phone_number, contact_email, contact_country, contact_subscribed_phone, contact_subscribed_email, contact_created_at, contact_last_interacted_at, contact_source, contact_tag, contact_engagement_rating, contact_emails_sent, contact_last_opened_at, contact_last_clicked_at, contact_unique_opens, contact_clicks, contact_open_rate, contact_click_rate, contact_suppressed, contact_suppression_reason, contact_bounced, contact_complained, contact_soft_bounce_count, contact_unsubscribed_at, contact_list. Catalogued custom and enrichment fields are exposed as contact_data_, with dots replaced by underscores (for example, data.attio.lifecycle_stage becomes contact_data_attio_lifecycle_stage).

predicatestringrequired

Ransack predicate: eq, not_eq, cont, not_cont, start, end, gt, lt, gteq, lteq, present, blank, true, false, in, not_in, within_days, not_within_days. Read /v1/my/flows/spec or nitro://schema for the predicates allowed by each filter type.

valueanyrequired

Filter value — string, number, boolean, array of strings for in/not_in predicates (e.g. ["engaged","warm"] for contact_engagement_rating in), or array of list ids for contact_list.

One of
predicatestringperformednot_performedrequired
predicatestringcount_at_leastcount_at_mostrequired
Example
{
  "operator": "and",
  "not": false,
  "children": [
    {
      "name": "string",
      "predicate": "string"
    }
  ]
}

SegmentFilterNode

object
One of
namestringrequired

Filter name from nitro://schema: contact_first_name, contact_last_name, contact_phone_number, contact_email, contact_country, contact_subscribed_phone, contact_subscribed_email, contact_created_at, contact_last_interacted_at, contact_source, contact_tag, contact_engagement_rating, contact_emails_sent, contact_last_opened_at, contact_last_clicked_at, contact_unique_opens, contact_clicks, contact_open_rate, contact_click_rate, contact_suppressed, contact_suppression_reason, contact_bounced, contact_complained, contact_soft_bounce_count, contact_unsubscribed_at, contact_list. Catalogued custom and enrichment fields are exposed as contact_data_, with dots replaced by underscores (for example, data.attio.lifecycle_stage becomes contact_data_attio_lifecycle_stage).

predicatestringrequired

Ransack predicate: eq, not_eq, cont, not_cont, start, end, gt, lt, gteq, lteq, present, blank, true, false, in, not_in, within_days, not_within_days. Read /v1/my/flows/spec or nitro://schema for the predicates allowed by each filter type.

valueanyrequired

Filter value — string, number, boolean, array of strings for in/not_in predicates (e.g. ["engaged","warm"] for contact_engagement_rating in), or array of list ids for contact_list.

One of
predicatestringperformednot_performedrequired
predicatestringcount_at_leastcount_at_mostrequired
BooleanSegmentFilterGroup
Example
{
  "name": "string",
  "predicate": "string"
}

BooleanSegmentFilterGroup

object
opstringandornotrequired
conditionsArray<any>required

Nested filter nodes. NOT groups must contain exactly one condition.

Example
{
  "op": "and",
  "conditions": []
}

SegmentFilters

array

Segment filters are validated fail-closed. Unknown filter names, globally invalid predicates, type-incompatible predicates, bad date values, invalid enum values, and unsupported rolling-window filters return a 422 invalid_filter error instead of silently widening an audience.

Array<AttributeSegmentFilter | any | any>
Example
[
  {
    "name": "string",
    "predicate": "string"
  }
]

SegmentPreview

object
countinteger

Live count of contacts matching the supplied filters.

sampleArray<object>

Bounded contact sample for quick verification.

Show child attributes
idinteger
emailstring | null
namestring | null
overlapArray<object>

Bounded overlap with existing segments.

Show child attributes
segment_idinteger
namestring
overlap_countinteger
Example
{
  "count": 0,
  "sample": [
    {
      "id": 0,
      "email": "string",
      "name": "string"
    }
  ],
  "overlap": [
    {
      "segment_id": 0,
      "name": "string",
      "overlap_count": 0
    }
  ]
}

AttributeSegmentFilter

object
namestringrequired

Filter name from nitro://schema: contact_first_name, contact_last_name, contact_phone_number, contact_email, contact_country, contact_subscribed_phone, contact_subscribed_email, contact_created_at, contact_last_interacted_at, contact_source, contact_tag, contact_engagement_rating, contact_emails_sent, contact_last_opened_at, contact_last_clicked_at, contact_unique_opens, contact_clicks, contact_open_rate, contact_click_rate, contact_suppressed, contact_suppression_reason, contact_bounced, contact_complained, contact_soft_bounce_count, contact_unsubscribed_at, contact_list. Catalogued custom and enrichment fields are exposed as contact_data_, with dots replaced by underscores (for example, data.attio.lifecycle_stage becomes contact_data_attio_lifecycle_stage).

predicatestringrequired

Ransack predicate: eq, not_eq, cont, not_cont, start, end, gt, lt, gteq, lteq, present, blank, true, false, in, not_in, within_days, not_within_days. Read /v1/my/flows/spec or nitro://schema for the predicates allowed by each filter type.

valueanyrequired

Filter value — string, number, boolean, array of strings for in/not_in predicates (e.g. ["engaged","warm"] for contact_engagement_rating in), or array of list ids for contact_list.

Example
{
  "name": "string",
  "predicate": "string"
}

EventSegmentFilter

object
One of
predicatestringperformednot_performedrequired
predicatestringcount_at_leastcount_at_mostrequired
Example
{
  "predicate": "performed"
}

Campaign

object
idinteger
account_idinteger
brand_idinteger | null
statusstringdraftactivepausedcompleted
approval_statestring
channelstringemailsms
namestring
dataobject
scheduled_atstring<date-time> | null
sent_countinteger
dashboard_urlstring<uri> | null

Canonical dashboard URL with the /my route prefix.

preview_urlstring<uri> | null

Signed, expiring public preview URL for the current template version.

recipient_snapshotobject | null

Last send snapshot. Values are captured at send time and are not a live audience estimate.

Show child attributes
requested_recipientsinteger | null
dispatched_recipientsinteger | null
blocked_recipientsinteger | null
requested_send_unitsinteger | null
dispatched_send_unitsinteger | null
units_per_recipientinteger | null
send_tokenstring | null
started_atstring<date-time> | null
completed_atstring<date-time> | null
last_send_recipientsinteger | null

Alias for the last dispatched recipient snapshot stored in data.recipients.

deliveryCampaignDeliverySummary | null

Present while a campaign has a current send token; use /delivery to poll, refresh progress, and receive polling metadata.

engagementEngagementBucket & object
editablebooleanread only

True when the campaign's content/audience/template can be edited. False for live, paused, completed, cancelled, archived, and for scheduled campaigns within 5 minutes of their scheduled_at. When false, PUT update accepts only name-only payloads and status transitions (Resume / Cancel); anything else returns 422 campaign_locked. Use POST /duplicate to fork into a new draft.

created_atstring<date-time>
updated_atstring<date-time>
triggerFlowTrigger
Show child attributes
idinteger
flow_idinteger
eventstring
audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target; null means no audience selected.

segment_idinteger | null
contact_list_idinteger | nulldeprecated

Deprecated — use contact_list_ids

contact_list_idsArray<integer>

Contact list IDs targeted by this trigger

exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set

exclude_contact_list_idsArray<integer>

Contact list IDs whose members are excluded from the recipient set

dataobject | null
triggered_countinteger
last_triggered_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
templateTemplate | null
templatesArray<Template>
Show child attributes
idinteger
namestring | null
flow_idinteger | null
action_idinteger | null
versioninteger
subjectstring | null
bodystring | null
preheaderstring | null
from_namestring | null
from_emailstring | null
reply_tostring | null
designEmailDesign | null
variablesobject
generation_idinteger | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "channel": "email",
  "name": "string",
  "data": {},
  "scheduled_at": "2024-01-15T09:30:00Z",
  "sent_count": 0,
  "dashboard_url": "https://example.com",
  "preview_url": "https://example.com",
  "recipient_snapshot": {
    "requested_recipients": 0,
    "dispatched_recipients": 0,
    "blocked_recipients": 0,
    "requested_send_units": 0,
    "dispatched_send_units": 0,
    "units_per_recipient": 0,
    "send_token": "string",
    "started_at": "2024-01-15T09:30:00Z",
    "completed_at": "2024-01-15T09:30:00Z"
  },
  "last_send_recipients": 0,
  "delivery": {
    "campaign_send_token": "string",
    "status": "sending",
    "recipients": 0,
    "sent": 0,
    "failed": 0,
    "pending": 0
  },
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "editable": true,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "trigger": {
    "id": 0,
    "flow_id": 0,
    "event": "string",
    "audience_type": "lists",
    "segment_id": 0,
    "contact_list_id": 0,
    "contact_list_ids": [
      0
    ],
    "exclude_segment_ids": [
      0
    ],
    "exclude_contact_list_ids": [
      0
    ],
    "data": {},
    "triggered_count": 0,
    "last_triggered_at": "2024-01-15T09:30:00Z",
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "template": {
    "id": 0,
    "name": "string",
    "flow_id": 0,
    "action_id": 0,
    "version": 0,
    "subject": "string",
    "body": "string",
    "preheader": "string",
    "from_name": "string",
    "from_email": "string",
    "reply_to": "string",
    "design": {
      "sections": [
        {
          "type": "header",
          "props": {},
          "styles": {
            "background_color": "string",
            "padding": "string",
            "align": "left",
            "font_size": 0,
            "text_color": "string",
            "border_radius": 0
          }
        }
      ],
      "theme": {
        "brand_color": "string",
        "bg_color": "string",
        "text_color": "string",
        "font_body": "string",
        "font_heading": "string",
        "logo_url": "string"
      }
    },
    "variables": {},
    "generation_id": 0,
    "created_at": "2024-01-15T09:30:00Z",
    "updated_at": "2024-01-15T09:30:00Z"
  },
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

CampaignDeliverySummary

object
campaign_send_tokenstring | nullrequired
statusstringsendingcompletedpausedrequired
recipientsintegerrequired

Recipient count captured for the active send.

sentintegerrequired
failedintegerrequired
pendingintegerrequired
Example
{
  "campaign_send_token": "string",
  "status": "sending",
  "recipients": 0,
  "sent": 0,
  "failed": 0,
  "pending": 0
}

CampaignDeliveryProgress

object
campaign_send_tokenstring | nullrequired
statusstringnot_startedsendingcompletedpausedrequired
recipientsintegerrequired

Recipient count captured for the active send.

sentintegerrequired
failedintegerrequired
pendingintegerrequired
terminalbooleanrequired

True when clients can stop polling.

poll_after_secondsinteger | nullrequired

Suggested polling delay for active sends.

Example
{
  "campaign_send_token": "string",
  "status": "not_started",
  "recipients": 0,
  "sent": 0,
  "failed": 0,
  "pending": 0,
  "terminal": true,
  "poll_after_seconds": 0
}

Message

object
idinteger
channelstringemailsms
tostring
subjectstring | null
statusstringqueuedsentfailed
provider_idstring | null
flow_idinteger | null

Source flow ID (null for transactional)

source_typestring | nullcampaignflowtest

campaign, flow, test, or null (transactional)

source_namestring | null

Name of source campaign or flow

sent_atstring<date-time> | null
created_atstring<date-time>
Example
{
  "id": 0,
  "channel": "email",
  "to": "string",
  "subject": "string",
  "status": "queued",
  "provider_id": "string",
  "flow_id": 0,
  "source_type": "campaign",
  "source_name": "string",
  "sent_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}

Suppression

object

Account suppression row with bounded source-event diagnostics.

idinteger
emailstring<email>
reasonstringhard_bouncesoft_bouncecomplaintmanualadmin
scopestringaccount_scoped
activeboolean
contact_idinteger | null
source_providerstring | null
source_event_idstring | null
provider_diagnosticstring | null

Bounded diagnostic text extracted from the provider feedback event, when retained.

bounce_typestring | nullhardsoft
bounce_subtypestring | null
complaint_feedback_typestring | null
event_occurred_atstring<date-time> | null
expires_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "email": "user@example.com",
  "reason": "hard_bounce",
  "scope": "account_scoped",
  "active": true,
  "contact_id": 0,
  "source_provider": "string",
  "source_event_id": "string",
  "provider_diagnostic": "string",
  "bounce_type": "hard",
  "bounce_subtype": "string",
  "complaint_feedback_type": "string",
  "event_occurred_at": "2024-01-15T09:30:00Z",
  "expires_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

ChatMessage

object
idinteger
rolestringuserassistanttoolsystem
contentstring
tool_callsArray<object>
sequenceinteger
created_atstring<date-time>
Example
{
  "id": 0,
  "role": "user",
  "content": "string",
  "tool_calls": [
    {}
  ],
  "sequence": 0,
  "created_at": "2024-01-15T09:30:00Z"
}

ChatSession

object
idinteger
account_idinteger
brand_idinteger
titlestring
statusstringactiveclosedfailed
started_atstring<date-time>
last_message_atstring<date-time>
message_countinteger
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "title": "string",
  "status": "active",
  "started_at": "2024-01-15T09:30:00Z",
  "last_message_at": "2024-01-15T09:30:00Z",
  "message_count": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

ChatSessionDetail

object
idinteger
account_idinteger
brand_idinteger
titlestring
statusstringactiveclosedfailed
started_atstring<date-time>
last_message_atstring<date-time>
message_countinteger
created_atstring<date-time>
updated_atstring<date-time>
messagesArray<ChatMessage>
Show child attributes
idinteger
rolestringuserassistanttoolsystem
contentstring
tool_callsArray<object>
sequenceinteger
created_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "title": "string",
  "status": "active",
  "started_at": "2024-01-15T09:30:00Z",
  "last_message_at": "2024-01-15T09:30:00Z",
  "message_count": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "messages": [
    {
      "id": 0,
      "role": "user",
      "content": "string",
      "tool_calls": [
        {}
      ],
      "sequence": 0,
      "created_at": "2024-01-15T09:30:00Z"
    }
  ]
}

Template

object
idinteger
namestring | null
flow_idinteger | null
action_idinteger | null
versioninteger
subjectstring | null
bodystring | null
preheaderstring | null
from_namestring | null
from_emailstring | null
reply_tostring | null
designEmailDesign | null
variablesobject
generation_idinteger | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "name": "string",
  "flow_id": 0,
  "action_id": 0,
  "version": 0,
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "from_name": "string",
  "from_email": "string",
  "reply_to": "string",
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "variables": {},
  "generation_id": 0,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

EmailStarter

object
idstring
categorystring
namestring
descriptionstring
iconstring
suggested_goalsArray<string>
designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring
preview_htmlstring | null
Example
{
  "id": "string",
  "category": "string",
  "name": "string",
  "description": "string",
  "icon": "string",
  "suggested_goals": [
    "string"
  ],
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "preview_html": "string"
}

TemplateSummary

object
idinteger
namestring | null
versioninteger
subjectstring | null
preheaderstring | null
flow_idinteger | null
section_countinteger
section_typesArray<string>
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "name": "string",
  "version": 0,
  "subject": "string",
  "preheader": "string",
  "flow_id": 0,
  "section_count": 0,
  "section_types": [
    "string"
  ],
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Flow

object
idinteger
account_idinteger
brand_idinteger | null
statusstringdraftlivepausedarchivedcancelled
approval_statestring
namestring
goalstring | null
triggerFlowTriggerOutput

Trigger as returned by Flow::Format.dump

Show child attributes
eventstring
segment_idinteger | null
contact_list_idinteger | null
exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set

dataobject | null
stepsArray<FlowStepOutput>
sent_countinteger
engagementEngagementBucket & object
created_atstring<date-time>
updated_atstring<date-time>
templatesArray<Template>
Show child attributes
idinteger
namestring | null
flow_idinteger | null
action_idinteger | null
versioninteger
subjectstring | null
bodystring | null
preheaderstring | null
from_namestring | null
from_emailstring | null
reply_tostring | null
designEmailDesign | null
variablesobject
generation_idinteger | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "brand_id": 0,
  "status": "draft",
  "approval_state": "string",
  "name": "string",
  "goal": "string",
  "trigger": {
    "event": "string",
    "segment_id": 0,
    "contact_list_id": 0,
    "exclude_segment_ids": [
      0
    ],
    "data": {}
  },
  "steps": [],
  "sent_count": 0,
  "engagement": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0,
    "account": {
      "sent": 0,
      "opens": 0,
      "total_opens": 0,
      "open_rate": 0
    }
  },
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "templates": [
    {
      "id": 0,
      "name": "string",
      "flow_id": 0,
      "action_id": 0,
      "version": 0,
      "subject": "string",
      "body": "string",
      "preheader": "string",
      "from_name": "string",
      "from_email": "string",
      "reply_to": "string",
      "design": {
        "sections": [
          {
            "type": "header",
            "props": {},
            "styles": {
              "background_color": "string",
              "padding": "string",
              "align": "left",
              "font_size": 0,
              "text_color": "string",
              "border_radius": 0
            }
          }
        ],
        "theme": {
          "brand_color": "string",
          "bg_color": "string",
          "text_color": "string",
          "font_body": "string",
          "font_heading": "string",
          "logo_url": "string"
        }
      },
      "variables": {},
      "generation_id": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z"
    }
  ]
}

FlowTemplate

object

Lean flow template card for index listings.

idstring

Template slug (e.g. welcome_series)

type_labelstring
descriptionstring
categorystring
email_countinteger
step_countinteger
preview_sectionsArray<object>

Section objects from the first email step's design, for template previews.

Example
{
  "id": "string",
  "type_label": "string",
  "description": "string",
  "category": "string",
  "email_count": 0,
  "step_count": 0,
  "preview_sections": [
    {}
  ]
}

FlowTemplateDetail

object

Lean flow template card for index listings.

idstring

Template slug (e.g. welcome_series)

type_labelstring
descriptionstring
categorystring
email_countinteger
step_countinteger
preview_sectionsArray<object>

Section objects from the first email step's design, for template previews.

Full flow template including the graph, ready for POST /v1/my/flows.

triggerobject

Trigger definition (pass as-is to POST /v1/my/flows trigger param)

stepsArray<object>

Step definitions (pass as-is to POST /v1/my/flows steps param)

Example
{
  "id": "string",
  "type_label": "string",
  "description": "string",
  "category": "string",
  "email_count": 0,
  "step_count": 0,
  "preview_sections": [
    {}
  ],
  "trigger": {},
  "steps": [
    {}
  ]
}

EngagementBucket

object

A (sent, opens, total_opens, open_rate) block. Used both for the resource itself and, nested as account, for the brand-level lifetime baseline.

sentintegerrequired

Total emails sent in this bucket.

opensintegerrequired

Unique human open events recorded in this bucket.

total_opensintegerrequired

All human open events including repeat opens by the same recipient, so total_opens >= opens. Falls back to opens when only unique-open data exists.

open_ratenumber<float> | nullrequired

Opens divided by sent, rounded to 4 decimal places. Null when sent is below the meaningful threshold for this bucket — zero for resource buckets, or below Report::Engagement::ACCOUNT_BASELINE_MIN_SENT for the account bucket.

Example
{
  "sent": 0,
  "opens": 0,
  "total_opens": 0,
  "open_rate": 0
}

Engagement

object

A (sent, opens, total_opens, open_rate) block. Used both for the resource itself and, nested as account, for the brand-level lifetime baseline.

sentintegerrequired

Total emails sent in this bucket.

opensintegerrequired

Unique human open events recorded in this bucket.

total_opensintegerrequired

All human open events including repeat opens by the same recipient, so total_opens >= opens. Falls back to opens when only unique-open data exists.

open_ratenumber<float> | nullrequired

Opens divided by sent, rounded to 4 decimal places. Null when sent is below the meaningful threshold for this bucket — zero for resource buckets, or below Report::Engagement::ACCOUNT_BASELINE_MIN_SENT for the account bucket.

accountEngagementBucketrequired

A (sent, opens, total_opens, open_rate) block. Used both for the resource itself and, nested as account, for the brand-level lifetime baseline.

Show child attributes
sentintegerrequired

Total emails sent in this bucket.

opensintegerrequired

Unique human open events recorded in this bucket.

total_opensintegerrequired

All human open events including repeat opens by the same recipient, so total_opens >= opens. Falls back to opens when only unique-open data exists.

open_ratenumber<float> | nullrequired

Opens divided by sent, rounded to 4 decimal places. Null when sent is below the meaningful threshold for this bucket — zero for resource buckets, or below Report::Engagement::ACCOUNT_BASELINE_MIN_SENT for the account bucket.

Example
{
  "sent": 0,
  "opens": 0,
  "total_opens": 0,
  "open_rate": 0,
  "account": {
    "sent": 0,
    "opens": 0,
    "total_opens": 0,
    "open_rate": 0
  }
}

FlowTrigger

object
idinteger
flow_idinteger
eventstring
audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target; null means no audience selected.

segment_idinteger | null
contact_list_idinteger | nulldeprecated

Deprecated — use contact_list_ids

contact_list_idsArray<integer>

Contact list IDs targeted by this trigger

exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set

exclude_contact_list_idsArray<integer>

Contact list IDs whose members are excluded from the recipient set

dataobject | null
triggered_countinteger
last_triggered_atstring<date-time> | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "flow_id": 0,
  "event": "string",
  "audience_type": "lists",
  "segment_id": 0,
  "contact_list_id": 0,
  "contact_list_ids": [
    0
  ],
  "exclude_segment_ids": [
    0
  ],
  "exclude_contact_list_ids": [
    0
  ],
  "data": {},
  "triggered_count": 0,
  "last_triggered_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

FlowTriggerInput

object
eventstring

Built-in: contact_add, keyword, message, list_add, list_remove, product_view, checkout, cart_add, cart_remove, cart_abandoned, browse_abandoned. Custom: any lowercase alphanumeric with underscores.

audience_typestring | nulllistssegmentall_contacts

Explicit campaign audience target; null means no audience selected.

segment_idinteger | null
contact_list_idinteger | nulldeprecated

Deprecated — use contact_list_ids

contact_list_idsArray<integer>

Contact list IDs to target

exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set; pass [] to clear

dataobject
Example
{
  "event": "string",
  "audience_type": "lists",
  "segment_id": 0,
  "contact_list_id": 0,
  "contact_list_ids": [
    0
  ],
  "exclude_segment_ids": [
    0
  ],
  "data": {}
}

FlowTriggerOutput

object

Trigger as returned by Flow::Format.dump

eventstring
segment_idinteger | null
contact_list_idinteger | null
exclude_segment_idsArray<integer>

Segment IDs whose matching contacts are excluded from the recipient set

dataobject | null
Example
{
  "event": "string",
  "segment_id": 0,
  "contact_list_id": 0,
  "exclude_segment_ids": [
    0
  ],
  "data": {}
}

FlowStepInput

object
typestringemailsmswaitsplitemit_eventrequired
subjectstring

Email step: subject line

bodystring

SMS body or email plain text

preheaderstring

Email step: preheader

from_namestring
from_emailstring
reply_tostring
designEmailDesign

Email template design document

Show child attributes
sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring
durationinteger

Wait step: seconds

filtersSegmentFilterExpression
yesArray<FlowStepInput>

Split step: yes branch

noArray<FlowStepInput>

Split step: no branch

event_namestring

Emit event step: event to fire

forward_event_databooleanfalse
event_dataobject
transactionalbooleanfalse
Example
{
  "type": "email",
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "from_name": "string",
  "from_email": "string",
  "reply_to": "string",
  "design": {
    "sections": [
      {
        "type": "header",
        "props": {},
        "styles": {
          "background_color": "string",
          "padding": "string",
          "align": "left",
          "font_size": 0,
          "text_color": "string",
          "border_radius": 0
        }
      }
    ],
    "theme": {
      "brand_color": "string",
      "bg_color": "string",
      "text_color": "string",
      "font_body": "string",
      "font_heading": "string",
      "logo_url": "string"
    }
  },
  "duration": 0,
  "yes": [],
  "no": [],
  "event_name": "string",
  "forward_event_data": false,
  "event_data": {},
  "transactional": false
}

FlowStepOutput

object

Step as returned by Flow::Format.dump

typestringemailsmswaitsplitemit_event
subjectstring
bodystring
preheaderstring
durationinteger
filtersArray<object>
yesArray<FlowStepOutput>
noArray<FlowStepOutput>
event_namestring
forward_event_databoolean
Example
{
  "type": "email",
  "subject": "string",
  "body": "string",
  "preheader": "string",
  "duration": 0,
  "filters": [
    {}
  ],
  "yes": [],
  "no": [],
  "event_name": "string",
  "forward_event_data": true
}

Event

object
idinteger
account_idinteger
contact_idinteger
user_idinteger
eventstring
amountnumber<double> | null
dataobject
idempotency_keystring | null
resource_uidstring | null
resource_namestring | null
resource_urlstring | null
testboolean
generatedboolean
chain_depthinteger
ipstring | null
user_agentstring | null
browserstring | null
osstring | null
device_typestring | null
referrerstring | null
utm_sourcestring | null
utm_mediumstring | null
utm_termstring | null
utm_contentstring | null
utm_campaignstring | null
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "account_id": 0,
  "contact_id": 0,
  "user_id": 0,
  "event": "string",
  "amount": 0,
  "data": {},
  "idempotency_key": "string",
  "resource_uid": "string",
  "resource_name": "string",
  "resource_url": "string",
  "test": true,
  "generated": true,
  "chain_depth": 0,
  "ip": "string",
  "user_agent": "string",
  "browser": "string",
  "os": "string",
  "device_type": "string",
  "referrer": "string",
  "utm_source": "string",
  "utm_medium": "string",
  "utm_term": "string",
  "utm_content": "string",
  "utm_campaign": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

Domain

object
idinteger
brand_idinteger | null
namestring
providerstringses
integration_idinteger | null
statusstringpendingverifiedfailed
dmarc_policystringnonequarantinereject

Effective observed DMARC policy persisted by Nitrosend; defaults to none until observed.

dmarc_recommended_policystringnonequarantinereject

Nitrosend's recommended DMARC policy rung.

dmarc_observed_policystring | nullnonequarantinereject

Last live DMARC policy observed by Nitrosend.

dns_recordsArray<object> | null
Show child attributes
record_typestring
namestring
valuestring
prioritystring | null
validstring
dns_healthobject | null
dns_setup_statusstringuncheckedincompletereadyverified
verified_atstring<date-time> | null
created_atstring<date-time>
Example
{
  "id": 0,
  "brand_id": 0,
  "name": "string",
  "provider": "ses",
  "integration_id": 0,
  "status": "pending",
  "dmarc_policy": "none",
  "dmarc_recommended_policy": "none",
  "dmarc_observed_policy": "none",
  "dns_records": [
    {
      "record_type": "string",
      "name": "string",
      "value": "string",
      "priority": "string",
      "valid": "string"
    }
  ],
  "dns_health": {},
  "dns_setup_status": "unchecked",
  "verified_at": "2024-01-15T09:30:00Z",
  "created_at": "2024-01-15T09:30:00Z"
}

EntriSessionResponse

object
domainobject
Show child attributes
idinteger
namestring
statusstringpendingverifiedfailed
entriobject
Show child attributes
application_idstring
tokenstring
prefilled_domainstring
user_idstring
dns_recordsArray<EntriDnsRecord>
Show child attributes
typestring
hoststring
valuestring
ttlinteger
priorityinteger | null
Example
{
  "domain": {
    "id": 0,
    "name": "string",
    "status": "pending"
  },
  "entri": {
    "application_id": "string",
    "token": "string",
    "prefilled_domain": "string",
    "user_id": "string",
    "dns_records": [
      {
        "type": "string",
        "host": "string",
        "value": "string",
        "ttl": 0,
        "priority": 0
      }
    ]
  }
}

EntriDnsRecord

object
typestring
hoststring
valuestring
ttlinteger
priorityinteger | null
Example
{
  "type": "string",
  "host": "string",
  "value": "string",
  "ttl": 0,
  "priority": 0
}

Integration

object
idinteger
providerstringmailgunsespostmarkresendsendgridtwilioattiohubspot
categorystringemailsmscrm
activeboolean
primaryboolean
statusstringpendingconnectederror
connected_atstring<date-time> | null
last_tested_atstring<date-time> | null
error_messagestring | null
config_summaryobject
secret_hintsobject
created_atstring<date-time>
updated_atstring<date-time>
Example
{
  "id": 0,
  "provider": "mailgun",
  "category": "email",
  "active": true,
  "primary": true,
  "status": "pending",
  "connected_at": "2024-01-15T09:30:00Z",
  "last_tested_at": "2024-01-15T09:30:00Z",
  "error_message": "string",
  "config_summary": {},
  "secret_hints": {},
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z"
}

IntegrationWriteRequest

object
integrationMailgunIntegrationInput | SesIntegrationInput | PostmarkIntegrationInput | ResendIntegrationInput | SendgridIntegrationInputrequired
Example
{
  "integration": {
    "provider": "mailgun",
    "api_key": "string",
    "domain": "string",
    "region": "string",
    "active": true
  }
}

MailgunIntegrationInput

object
providerstringmailgunrequired
api_keystringrequired
domainstringrequired

Mailgun sending domain

regionstring | null

Optional Mailgun region hint

activebooleantrue
Example
{
  "provider": "mailgun",
  "api_key": "string",
  "domain": "string",
  "region": "string",
  "active": true
}

SesIntegrationInput

object
providerstringsesrequired
access_key_idstringrequired
secret_access_keystringrequired
regionstringrequired

AWS SES region

activebooleantrue
Example
{
  "provider": "ses",
  "access_key_id": "string",
  "secret_access_key": "string",
  "region": "string",
  "active": true
}

PostmarkIntegrationInput

object
providerstringpostmarkrequired
server_tokenstringrequired

Postmark server token for sending email

account_tokenstringrequired

Postmark account token for domains API access

activebooleantrue
Example
{
  "provider": "postmark",
  "server_token": "string",
  "account_token": "string",
  "active": true
}

ResendIntegrationInput

object
providerstringresendrequired
api_keystringrequired
activebooleantrue
Example
{
  "provider": "resend",
  "api_key": "string",
  "active": true
}

SendgridIntegrationInput

object
providerstringsendgridrequired
api_keystringrequired
activebooleantrue
Example
{
  "provider": "sendgrid",
  "api_key": "string",
  "active": true
}

Plan

object
idinteger
namestring
activeboolean
Example
{
  "id": 0,
  "name": "string",
  "active": true
}

EmailDesign

object

Email template design document

sectionsArray<EmailSection>
Show child attributes
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
themeobject

Theme overrides merged on top of brand theme

Show child attributes
brand_colorstring
bg_colorstring
text_colorstring
font_bodystring
font_headingstring
logo_urlstring
Example
{
  "sections": [
    {
      "type": "header",
      "props": {},
      "styles": {
        "background_color": "string",
        "padding": "string",
        "align": "left",
        "font_size": 0,
        "text_color": "string",
        "border_radius": 0
      }
    }
  ],
  "theme": {
    "brand_color": "string",
    "bg_color": "string",
    "text_color": "string",
    "font_body": "string",
    "font_heading": "string",
    "logo_url": "string"
  }
}

EmailAccessibilityLint

object

Advisory accessibility lint result for rendered email previews

validboolean

Always true while accessibility lint is advisory-only

warningsArray<EmailAccessibilityWarning>
Show child attributes
levelstring
rulestring
messagestring
suggested_fixstring
countinteger
min_rationumber<float>
Example
{
  "valid": true,
  "warnings": [
    {
      "level": "warning",
      "rule": "image_alt_text",
      "message": "string",
      "suggested_fix": "string",
      "count": 0,
      "min_ratio": 0
    }
  ]
}

EmailAccessibilityWarning

object
levelstring
rulestring
messagestring
suggested_fixstring
countinteger
min_rationumber<float>
Example
{
  "level": "warning",
  "rule": "image_alt_text",
  "message": "string",
  "suggested_fix": "string",
  "count": 0,
  "min_ratio": 0
}

EmailSection

object
typestringheaderherotextimagebuttoncolumnsproductsocialdividerspacerfooterrequired
propsobject

Section-specific properties (see email component spec)

stylesobject
Show child attributes
background_colorstring
paddingstring
alignstringleftcenterright
font_sizeinteger
text_colorstring
border_radiusinteger
Example
{
  "type": "header",
  "props": {},
  "styles": {
    "background_color": "string",
    "padding": "string",
    "align": "left",
    "font_size": 0,
    "text_color": "string",
    "border_radius": 0
  }
}

EmailComponentSpec

object

Full schema for email design sections. Each component has type, description, props (with types, required flags, defaults), and tips.

versioninteger
design_guidelinesstring
componentsArray<object>
Show child attributes
typestring
descriptionstring
tipsArray<string>
propsobject
style_attributesArray<object>

Standard per-section style attribute registry.

Show child attributes
keystring
labelstring
typestringcolorspacingenumnumber
applies_toany
theme_fallbackstring
descriptionstring
valuesArray<string>
targetobject
Show child attributes
elstringsectioncontent
attrstring
variablesArray<string>
theme_attributesArray<object>

Brand Kit theme attribute registry (single source of truth for the editor).

Show child attributes
keystring
labelstring
typestringcolorfontnumberenumstringimage
categorystringcolorfontvisual_identityidentity
slotstring
surfacesArray<string>
mininteger
maxinteger
valuesArray<string>
optionsArray<object>
Show child attributes
valuestring
labelstring
descriptionstring
transform_tableobject
columnboolean
value_resolverstring
descriptionstring
Example
{
  "version": 0,
  "design_guidelines": "string",
  "components": [
    {
      "type": "string",
      "description": "string",
      "tips": [
        "string"
      ],
      "props": {}
    }
  ],
  "style_attributes": [
    {
      "key": "string",
      "label": "string",
      "type": "color",
      "theme_fallback": "string",
      "description": "string",
      "values": [
        "string"
      ],
      "target": {
        "el": "section",
        "attr": "string"
      }
    }
  ],
  "variables": [
    "string"
  ],
  "theme_attributes": [
    {
      "key": "string",
      "label": "string",
      "type": "color",
      "category": "color",
      "slot": "string",
      "surfaces": [
        "string"
      ],
      "min": 0,
      "max": 0,
      "values": [
        "string"
      ],
      "options": [
        {
          "value": "string",
          "label": "string",
          "description": "string"
        }
      ],
      "transform_table": {},
      "column": true,
      "value_resolver": "string",
      "description": "string"
    }
  ]
}

FlowSpec

object

Schema for flow step types, trigger events, and segment filter names. Note: the raw response includes internal fields (icon, visible, inputs, outputs, alias) used by the flow editor UI — these can be ignored by API consumers.

filtersobject
triggersArray<object>
Show child attributes
titlestring
eventstring
stepsArray<object>
Show child attributes
typestring
titlestring
summarystring
paramsobject
lifecycle_flowsArray<object>

Canonical lifecycle flow templates for the flow picker.

Show child attributes
idstring
keystring
goalstring
namestring
descriptionstring
priorityinteger
triggerobject
Show child attributes
eventstring
trigger_needsstring | null
stepsArray<object>
Show child attributes
typestring
durationinteger

Wait duration in seconds for wait steps.

subjectstring
preheaderstring
bodystring
designobject
Example
{
  "filters": {},
  "triggers": [
    {
      "title": "string",
      "event": "string"
    }
  ],
  "steps": [
    {
      "type": "string",
      "title": "string",
      "summary": "string",
      "params": {}
    }
  ],
  "lifecycle_flows": [
    {
      "id": "string",
      "key": "string",
      "goal": "string",
      "name": "string",
      "description": "string",
      "priority": 0,
      "trigger": {
        "event": "string"
      },
      "trigger_needs": "string",
      "steps": [
        {
          "type": "string",
          "duration": 0,
          "subject": "string",
          "preheader": "string",
          "body": "string",
          "design": {}
        }
      ]
    }
  ]
}