Developers

API Documentation

One REST API across identity verification, email encryption & DLP, mailbox management, and the leads marketplace. JSON in, JSON out, bearer-authenticated.

Introduction

The Bastion API is organized around REST. All requests use HTTPS and accept and return JSON. Each product line is versioned independently — the version prefix appears in every endpoint’s path (you’ll see /v3, /v2, and /v1 across the products below). Every response includes a request_id for support.

Your base URL is tenant-specific

Requests are issued against your organization’s dedicated base URL. It is not published here. Contact integrations@bastioncommunications.com to receive your tenant base URL, then set it as BASE_URL / BASTION_BASE_URL in the examples below.

Enterprise accounts are hierarchical. Your account is the parent tenant, and you can nest sub-organizations beneath it (see Organizations & Tenancy). Scope any request to a sub-tenant by sending the Bastion-Org-Id header; omit it to act on the parent tenant.

Building an integration? Request to join the integration marketplace →

Authentication

Given the sensitivity of the data it protects, Bastion uses a layered, mutual-approval authentication model. Every request must satisfy all four controls below — a bearer key alone is never sufficient.

1 · Approved source IPs (mandatory)

Requests are accepted only from IP addresses pre-registered to your credentials. Traffic from any other address is rejected with 403 before processing. Allowlisted IPs are registered and rotated with your account team.

2 · Client X.509 certificate (mandatory)

Present a client X.509 digital certificate issued by an approved public certificate authority (for example, IdenTrust) that has independently verified your company's identity. You register the certificate with Bastion; calls are then mutually authenticated (mTLS) and each request is signed with the certificate's private key, validated against your registered chain on every call.

3 · Scoped bearer key

Your scoped API key is presented as a bearer token (and per sub-tenant via the Bastion-Org-Id header). The key identifies the credential; the certificate and source IP prove it.

4 · Out-of-band request approval

For sensitive operations Bastion does not execute immediately — it sends a signed authorization request to your approval webhook, and only an approved, correctly-signed response (carrying your internal approval id) releases the original request.

Request headers

Authorization:        Bearer $BASTION_API_KEY
Bastion-Org-Id:       org_7f31aa                 # optional: act as a sub-tenant
Bastion-Cert-Thumbprint: 9F:2C:1A:9E:...:44      # SHA-256 thumbprint of your X.509 cert
Bastion-Timestamp:    1783000577                 # unix seconds (rejected if skew > 60s)
Bastion-Nonce:        b7d3c1a9e0f2               # single-use, replay-protected
Bastion-Signature:    <base64 X.509 signature of the canonical digest>

Canonical request digest (X.509-signed)

# Canonical request digest — signed with your X.509 private key.
digest = SHA256(
  METHOD           + "\n" +
  PATH             + "\n" +
  BASTION_TIMESTAMP+ "\n" +
  BASTION_NONCE    + "\n" +
  SHA256(raw_body)
)
Bastion-Signature = base64( sign(digest, x509_private_key) )

Out-of-band request approval

When a request for a sensitive operation arrives from an approved IP, Bastion issues an outbound authorization request — a description of your original request — to your registered approval webhook(s). Your system reviews it and returns an approval carrying your own internal approval id (for your audit trail), bound to the request by its SHA-256 digest and signed with your X.509 certificate. Only then does Bastion execute the original request.

① Bastion → your approval webhook

// Outbound from Bastion → your registered approval webhook(s).
// Bastion does NOT execute the original request until you approve it.
{
  "authorization_request_id": "areq_a1b2c3",
  "requesting_ip": "203.0.113.24",
  "received_request": {
    "method": "POST",
    "path": "/v3/secure-email/send",
    "digest": "sha256:7d21e0a4c9…"
  },
  "requested_at": "2026-07-02T14:03:00Z",
  "expires_at":   "2026-07-02T14:04:00Z"
}

② Your webhook → Bastion (approval)

// Your webhook's response — assigns YOUR internal approval id for audit,
// bound to the request digest and signed with your X.509 certificate.
{
  "authorization_request_id": "areq_a1b2c3",
  "decision": "approved",
  "customer_approval_id": "CHG-2026-000481",
  "digest": "sha256:7d21e0a4c9…",
  "signature": "<base64 X.509 signature of digest + customer_approval_id>"
}

Transport

Integrate over the public internet (REST/JSON) or over a private extranet using SOAP 1.2 + WSDL, with HTTP MIME Multipart envelopes carrying the signed request and X.509 certificate. The WSDL is provisioned with your tenant; obtain your X.509 client certificate from an approved CA such as IdenTrust and register it with us. Contact integrations@bastioncommunications.com.

Errors & limits

Standard HTTP status codes are used. Errors return { error, request_id, errors[] }. List endpoints are cursor-paginated via next_cursor. Rate limits are returned in X-RateLimit-* headers; exceeding them yields 429.

400Malformed request — invalid JSON or missing required field.
401Missing or invalid API key.
403The key lacks the required scope for this endpoint.
404Resource not found on your tenant.
422Validation failed — see the errors array in the body.
429Rate limited — retry after the Retry-After header.

Webhooks & signatures

Subscribe to events with the Webhooks API. Every delivery is an HTTPS POST carrying Bastion-Timestamp and Bastion-Signature (a hex HMAC-SHA256). Verify each delivery by recomputing the HMAC over `${timestamp}.${rawBody}`with the subscription’s signing secret, comparing in constant time, and rejecting timestamps older than five minutes.

Verify a webhook signature (Node)

import crypto from "node:crypto";

// Verify a Bastion webhook (headers: Bastion-Signature, Bastion-Timestamp).
export function verify(rawBody, headers, signingSecret) {
  const ts = headers["bastion-timestamp"];
  const sig = headers["bastion-signature"];
  // Reject replays older than 5 minutes.
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}

Event catalog

message.deliveredAn outbound message was accepted by the recipient server.
message.openedA recipient opened a tracked message.
message.clickedA recipient clicked a tracked link.
message.bouncedA message hard- or soft-bounced.
message.complainedA recipient marked a message as spam.
message.receivedA new inbound message arrived in a mailbox.
message.scannedA message finished threat + DLP scanning.
dlp.matchOutbound content matched a DLP lexicon.
mailbox.createdA mailbox was provisioned.
mailbox.suspendedA mailbox was suspended.
org.createdA sub-organization was created.
verification.completedAn identity verification finished.
lead.purchasedA lead was purchased.
campaign.completedA marketing campaign finished sending.

Organizations & Tenancy

v3

Model enterprise hierarchies. Nest sub-organizations (sub-tenants) beneath your account, structure them with organizational units and teams, verify domains, and issue per-tenant, scoped API keys.

POST/v3/organizations

Create a sub-organization

Creates a nested sub-tenant beneath your account (or another organization). Sub-organizations isolate mailboxes, contacts, DLP policies, API keys, and billing — ideal for business units, resellers, or enterprise customers.

Scope: org:write

Body parameters

FieldTypeReq.Description
namestringyesDisplay name of the organization.
parent_idstringParent org id. Omit to nest directly under your account.
external_idstringYour own identifier, for reconciliation.
planstringPlan/tier to provision the sub-tenant on.
data_regionstringData residency region, e.g. us | eu | ca.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Stonebridge Health — East",
  "parent_id": "org_root",
  "external_id": "CUST-4021",
  "plan": "enterprise",
  "data_region": "us"
}'

Response · 200

{
  "org_id": "org_7f31aa",
  "name": "Stonebridge Health — East",
  "parent_id": "org_root",
  "path": "org_root/org_7f31aa",
  "status": "active",
  "created_at": "2026-07-02T14:03:00Z"
}
GET/v3/organizations

List organizations

Returns organizations in your hierarchy. Filter by parent to walk the tree one level at a time; the path field shows each org's ancestry.

Scope: org:read

Query parameters

FieldTypeReq.Description
parent_idstringReturn direct children of this org.
limitintegerPage size, 1–200.
cursorstringPagination cursor.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/organizations?parent_id=<parent_id>&limit=<limit>&cursor=<cursor>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "organizations": [
    {
      "org_id": "org_7f31aa",
      "name": "Stonebridge Health — East",
      "parent_id": "org_root",
      "path": "org_root/org_7f31aa",
      "status": "active",
      "mailbox_count": 42
    }
  ],
  "next_cursor": null
}
GET/v3/organizations/{org_id}

Retrieve an organization

Fetches a single organization including settings, quota usage, and its position in the hierarchy.

Scope: org:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/organizations/{org_id}" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "org_id": "org_7f31aa",
  "name": "Stonebridge Health — East",
  "parent_id": "org_root",
  "path": "org_root/org_7f31aa",
  "status": "active",
  "settings": {
    "enforce_mfa": true,
    "default_dlp_policy": "pol_hipaa_default"
  },
  "usage": {
    "mailboxes": 42,
    "seats": 60
  }
}
PATCH/v3/organizations/{org_id}

Update an organization

Renames an organization, merges settings, or suspends/reactivates the sub-tenant. Suspending disables the org's keys and mailboxes without deleting data.

Scope: org:write

Body parameters

FieldTypeReq.Description
namestringNew display name.
statusstringactive | suspended.
settingsobjectSettings to merge, e.g. { enforce_mfa, default_dlp_policy }.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PATCH "$BASE_URL/v3/organizations/{org_id}" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "active",
  "settings": {
    "enforce_mfa": true
  }
}'

Response · 200

{
  "org_id": "org_7f31aa",
  "status": "active",
  "settings": {
    "enforce_mfa": true
  }
}
POST/v3/organizations/{org_id}/org-units

Create an organizational unit

Creates a nested organizational unit (OU) to structure users and apply policies (DLP, retention, routing) by department or location.

Scope: org:write

Body parameters

FieldTypeReq.Description
namestringyesOU name, e.g. Clinical.
parent_ou_idstringParent OU to nest under. Omit for the org root.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations/{org_id}/org-units" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Clinical",
  "parent_ou_id": null
}'

Response · 200

{
  "ou_id": "ou_31b2",
  "name": "Clinical",
  "path": "/Clinical"
}
POST/v3/organizations/{org_id}/domains

Add & verify a domain

Registers a sending/receiving domain for the organization and returns the DNS records (TXT, MX, DKIM) to publish for verification.

Scope: org:write

Body parameters

FieldTypeReq.Description
domainstringyesDomain to add, e.g. stonebridge.health.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations/{org_id}/domains" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "stonebridge.health"
}'

Response · 200

{
  "domain_id": "dom_88fa",
  "domain": "stonebridge.health",
  "status": "pending",
  "dns_records": [
    {
      "type": "TXT",
      "host": "@",
      "value": "bastion-verify=8f2c1a9e"
    },
    {
      "type": "MX",
      "host": "@",
      "value": "mx.tenant.mail",
      "priority": 10
    },
    {
      "type": "TXT",
      "host": "bastion._domainkey",
      "value": "v=DKIM1; k=rsa; p=MIGf…"
    }
  ]
}
POST/v3/organizations/{org_id}/members

Invite a member

Invites a user to an organization with a role. Roles control access across mailboxes, DLP, leads, and sub-organization administration.

Scope: org:write

Body parameters

FieldTypeReq.Description
emailstringyesInvitee email address.
rolestringyesowner | admin | member | readonly, or a custom role id.
ou_idstringOrganizational unit to place the member in.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations/{org_id}/members" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "admin@stonebridge.health",
  "role": "admin",
  "ou_id": "ou_31b2"
}'

Response · 200

{
  "member_id": "mem_5a12",
  "email": "admin@stonebridge.health",
  "role": "admin",
  "status": "invited"
}
POST/v3/organizations/{org_id}/roles

Create a custom role

Defines a custom role from a set of scopes for fine-grained, least-privilege access within an organization.

Scope: org:write

Body parameters

FieldTypeReq.Description
namestringyesRole name.
scopesstring[]yesScopes granted by the role.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations/{org_id}/roles" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Billing clerk",
  "scopes": [
    "secure_email:read",
    "messages:read"
  ]
}'

Response · 200

{
  "role_id": "role_2c9d",
  "name": "Billing clerk",
  "scopes": [
    "secure_email:read",
    "messages:read"
  ]
}
POST/v3/organizations/{org_id}/api-keys

Issue a scoped API key

Creates an API key bound to a single organization and scope set. Requests made with the key are automatically restricted to that sub-tenant. The secret is shown only once.

Scope: org:write

Body parameters

FieldTypeReq.Description
namestringyesLabel for the key.
scopesstring[]yesScopes the key may use.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/organizations/{org_id}/api-keys" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "East region server",
  "scopes": [
    "identity:read",
    "secure_email:write"
  ]
}'

Response · 200

{
  "key_id": "key_9f0a",
  "org_id": "org_7f31aa",
  "scopes": [
    "identity:read",
    "secure_email:write"
  ],
  "secret": "sk_live_… (shown once)",
  "created_at": "2026-07-02T14:03:00Z"
}

API Keys & Access

v3

Issue keys with least-privilege, resource:action scopes to micro-manage exactly what each integration can do. Every key still inherits your organization's X.509 mutual authentication and IP allowlist — and marketplace partners are independently vetted before a key is ever issued.

POST/v3/api-keys

Create an API key

Mints an API key restricted to an explicit set of scopes (and, optionally, a single sub-organization, a tighter IP allowlist, and an expiry). Grant only the scopes an integration needs — the secret is returned once and never again. The key holder remains bound by your X.509 mutual auth and approved source IPs.

Scope: keys:write

Body parameters

FieldTypeReq.Description
namestringyesLabel for the key (shown in audit logs).
scopesstring[]yesGranular resource:action scopes this key may use (see GET /v3/scopes).
org_idstringRestrict the key to a single sub-organization.
ip_allowliststring[]Further narrow the key to specific source IPs/CIDRs.
expires_atstringISO 8601 expiry. Omit for a non-expiring key.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/api-keys" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Verification service (read-only)",
  "scopes": [
    "verify:read",
    "compliance:read"
  ],
  "org_id": "org_7f31aa",
  "ip_allowlist": [
    "203.0.113.24/32"
  ],
  "expires_at": "2027-07-02T00:00:00Z"
}'

Response · 200

{
  "key_id": "key_9f0a",
  "name": "Verification service (read-only)",
  "scopes": [
    "verify:read",
    "compliance:read"
  ],
  "org_id": "org_7f31aa",
  "secret": "sk_live_… (shown once)",
  "created_at": "2026-07-02T14:03:00Z"
}
GET/v3/api-keys

List API keys

Lists keys with their scopes, org binding, and last-used timestamp. Secrets are never returned.

Scope: keys:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/api-keys" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "keys": [
    {
      "key_id": "key_9f0a",
      "name": "Verification service (read-only)",
      "scopes": [
        "verify:read",
        "compliance:read"
      ],
      "last_used_at": "2026-07-02T13:59:00Z"
    }
  ]
}
POST/v3/api-keys/{key_id}/rotate

Rotate an API key

Issues a new secret for the key. The previous secret stays valid for a 24-hour overlap for zero-downtime rollout.

Scope: keys:write

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/api-keys/{key_id}/rotate" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "key_id": "key_9f0a",
  "secret": "sk_live_new… (shown once)",
  "previous_valid_until": "2026-07-03T14:03:00Z"
}
DELETE/v3/api-keys/{key_id}

Revoke an API key

Immediately and permanently revokes a key. In-flight requests using it are rejected.

Scope: keys:write

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X DELETE "$BASE_URL/v3/api-keys/{key_id}" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "key_id": "key_9f0a",
  "status": "revoked"
}
GET/v3/scopes

List available scopes

Returns the full catalog of granular scopes, grouped by product, so you can assemble a least-privilege key. Scopes follow a resource:action pattern.

Scope: keys:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/scopes" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "scopes": [
    {
      "scope": "verify:read",
      "product": "Contact Verification",
      "grants": "Run verification and compliance screens."
    },
    {
      "scope": "secure_email:read",
      "product": "Email Encryption",
      "grants": "Read message status and audit events."
    },
    {
      "scope": "secure_email:write",
      "product": "Email Encryption",
      "grants": "Send and recall encrypted mail."
    },
    {
      "scope": "dlp:read",
      "product": "Sensitive Data Protection",
      "grants": "Scan content and read findings."
    },
    {
      "scope": "dlp:write",
      "product": "Sensitive Data Protection",
      "grants": "Transform, redact, and manage detectors/profiles."
    },
    {
      "scope": "dlp:restore",
      "product": "Sensitive Data Protection",
      "grants": "Re-identify tokenized content (high-privilege)."
    },
    {
      "scope": "mailboxes:write",
      "product": "Mailbox Management",
      "grants": "Provision and manage mailboxes."
    },
    {
      "scope": "messages:write",
      "product": "Mailbox Management",
      "grants": "Send mail from a mailbox."
    },
    {
      "scope": "directory:write",
      "product": "Mailbox Management",
      "grants": "Manage contacts, lists, and distribution lists."
    },
    {
      "scope": "marketing:write",
      "product": "Marketing & Campaigns",
      "grants": "Create and send campaigns."
    },
    {
      "scope": "leads:write",
      "product": "Leads Marketplace",
      "grants": "Purchase and post leads."
    },
    {
      "scope": "webhooks:write",
      "product": "Webhooks",
      "grants": "Manage webhook subscriptions."
    },
    {
      "scope": "org:write",
      "product": "Organizations",
      "grants": "Manage sub-organizations, members, and roles."
    },
    {
      "scope": "keys:write",
      "product": "API Keys & Access",
      "grants": "Create, rotate, and revoke API keys."
    }
  ]
}

Contact Verification

v2

Validate a phone, email, and address, and screen contacts for outreach compliance — in real time, to reduce fraud and wrong-party contact.

POST/v2/verify/contact

Verify a contact

Validates a phone, email, address, and name together and returns a per-field verification status plus an overall confidence score (0–100). Confirm a contact is real before you reach out.

Scope: verify:read

Body parameters

FieldTypeReq.Description
phonestringyesE.164 phone number, e.g. +12065550142.
emailstringEmail address to validate.
namestringFull name to match.
addressobjectPostal address { line1, city, state, postal_code }.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/verify/contact" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "+12065550142",
  "email": "dana@stonebridge.health",
  "name": "Dana Okafor",
  "address": {
    "line1": "500 Market St",
    "city": "Seattle",
    "state": "WA",
    "postal_code": "98104"
  }
}'

Response · 200

{
  "request_id": "req_8f2c1a9e",
  "confidence": 96,
  "phone": {
    "status": "verified",
    "type": "mobile",
    "carrier": "Example Wireless",
    "prepaid": false,
    "reachable": true
  },
  "email": {
    "status": "verified",
    "deliverable": true,
    "disposable": false
  },
  "address": {
    "status": "partial",
    "match": "street"
  },
  "name": {
    "status": "verified",
    "match": "exact"
  }
}
POST/v2/verify/phone

Check a phone number

Returns validity, number type, carrier, prepaid status, and a reachability confidence score (0–100).

Scope: verify:read

Body parameters

FieldTypeReq.Description
phonestringyesE.164 phone number to check.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/verify/phone" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "+12065550142"
}'

Response · 200

{
  "request_id": "req_1b7d33af",
  "valid": true,
  "type": "mobile",
  "carrier": "Example Wireless",
  "prepaid": false,
  "reachable": true,
  "confidence": 91,
  "country_code": "US"
}
GET/v2/verify/phone-owner

Look up a phone owner

Resolves a phone number to the current registered owner and region, for right-party contact and caller identification.

Scope: verify:read

Query parameters

FieldTypeReq.Description
phonestringyesE.164 phone number to look up.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v2/verify/phone-owner?phone=+12065550142" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "request_id": "req_44aa10c2",
  "name": "Dana Okafor",
  "region": {
    "city": "Seattle",
    "state": "WA"
  },
  "type": "mobile",
  "carrier": "Example Wireless"
}
POST/v2/verify/email

Check an email address

Checks deliverability, MX presence, and disposable/role-account signals, and returns a risk level. Use before adding to a sending list.

Scope: verify:read

Body parameters

FieldTypeReq.Description
emailstringyesEmail address to check.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/verify/email" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dana@stonebridge.health"
}'

Response · 200

{
  "request_id": "req_9c0e77b1",
  "deliverable": true,
  "mx_found": true,
  "disposable": false,
  "role_account": false,
  "risk": "low"
}
POST/v2/compliance/screen

Screen a number for outreach

Screens a number against national and state Do-Not-Call registries and returns an outreach recommendation to gate calling and texting.

Scope: compliance:read

Body parameters

FieldTypeReq.Description
phonestringyesE.164 phone number to screen.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/compliance/screen" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "+12065550142"
}'

Response · 200

{
  "request_id": "req_2f56ab90",
  "dnc_national": false,
  "dnc_state": false,
  "litigation_risk": "low",
  "recommendation": "allow"
}

Email Encryption

v3

Policy-based encryption for outbound mail — the right delivery method per recipient, with recall, expiration, and a full audit trail.

POST/v3/secure-email/send

Send an encrypted message

Applies your DLP policy and delivers via the best method for each recipient — transparent TLS, secure portal, or push — with optional recall and expiration.

Scope: secure_email:write

Body parameters

FieldTypeReq.Description
fromstringyesVerified sender address on your domain.
tostring[]yesOne or more recipient addresses.
subjectstringyesMessage subject.
body_htmlstringyesHTML body of the message.
policystringDLP policy id to apply. Defaults to your org policy.
expires_inintegerSeconds until the message expires for recipients.
allow_recallbooleanWhether the sender may recall the message.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/secure-email/send" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "billing@stonebridge.health",
  "to": [
    "patient@example.com"
  ],
  "subject": "Your statement",
  "body_html": "<p>Your latest statement is attached.</p>",
  "policy": "pol_hipaa_default",
  "expires_in": 604800,
  "allow_recall": true
}'

Response · 200

{
  "message_id": "msg_7d21e0a4",
  "status": "sent",
  "delivery_method": "tls",
  "dlp_action": "encrypt",
  "expires_at": "2026-07-09T14:03:00Z"
}
POST/v3/secure-email/{message_id}/recall

Recall a message

Revokes recipient access to a previously sent encrypted message, even after delivery. Portal and push recipients lose access immediately.

Scope: secure_email:write

Body parameters

FieldTypeReq.Description
reasonstringOptional audit reason for the recall.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/secure-email/{message_id}/recall" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Sent to wrong recipient"
}'

Response · 200

{
  "message_id": "msg_7d21e0a4",
  "status": "recalled",
  "recalled_at": "2026-07-02T15:10:00Z"
}
GET/v3/secure-email/{message_id}/events

Get message audit events

Returns the delivery and access trail for a message — delivered, opened, decrypted, expired, recalled — for compliance evidence.

Scope: secure_email:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/secure-email/{message_id}/events" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "message_id": "msg_7d21e0a4",
  "events": [
    {
      "type": "delivered",
      "recipient": "patient@example.com",
      "at": "2026-07-02T14:03:05Z"
    },
    {
      "type": "opened",
      "recipient": "patient@example.com",
      "at": "2026-07-02T14:20:41Z"
    },
    {
      "type": "decrypted",
      "recipient": "patient@example.com",
      "at": "2026-07-02T14:20:44Z"
    }
  ]
}

Sensitive Data Protection

v3

Classify, redact, tokenize, and continuously monitor sensitive data across mail and content. A mail-native, ML-assisted evolution of data-loss prevention — inline on send, on demand, or continuously across mailboxes.

GET/v3/dlp/detectors

List detectors

Returns the built-in detectors (classifiers) available for scanning — each with a category and the confidence model it uses (pattern, dictionary, or ML/context).

Scope: dlp:read

Query parameters

FieldTypeReq.Description
categorystringFilter by category, e.g. health | financial | identity.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/dlp/detectors?category=<category>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "detectors": [
    {
      "id": "us_ssn",
      "name": "US Social Security Number",
      "category": "identity",
      "model": "pattern+context"
    },
    {
      "id": "mrn",
      "name": "Medical Record Number",
      "category": "health",
      "model": "ml"
    },
    {
      "id": "pan",
      "name": "Payment Card Number",
      "category": "financial",
      "model": "pattern+checksum"
    }
  ]
}
POST/v3/dlp/scan

Scan content

Inspects text or a message for sensitive data and returns per-finding location, confidence, and severity, plus a recommended action. Runs inline on send or on demand.

Scope: dlp:read

Body parameters

FieldTypeReq.Description
contentstringRaw text to scan. Provide this or message_id.
message_idstringScan an existing message instead of inline text.
detectorsstring[]Detector ids to run. Defaults to your active profile.
min_confidencestringMinimum reported confidence: low | medium | high.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/scan" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "Member SSN 123-45-6789, DOB 1984-02-11.",
  "detectors": [
    "us_ssn",
    "date_of_birth"
  ],
  "min_confidence": "low"
}'

Response · 200

{
  "request_id": "req_5aa3ed10",
  "findings": [
    {
      "detector": "us_ssn",
      "quote_hash": "sha256:1a…",
      "offset": 11,
      "length": 11,
      "confidence": "high",
      "severity": "high"
    },
    {
      "detector": "date_of_birth",
      "offset": 27,
      "length": 10,
      "confidence": "medium",
      "severity": "medium"
    }
  ],
  "summary": {
    "total": 2,
    "max_severity": "high"
  },
  "recommended_action": "encrypt"
}
POST/v3/dlp/transform

Transform (de-identify) content

Removes or obscures sensitive data using per-detector transformations — mask, redact, hash, date-shift, or reversible format-preserving tokenization. Returns a token map id when any transform is reversible.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
contentstringyesText to de-identify.
transformationsobject[]yesPer-detector rules [{ detector, method, options }].

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/transform" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "SSN 123-45-6789, card 4111 1111 1111 1111.",
  "transformations": [
    {
      "detector": "us_ssn",
      "method": "mask",
      "options": {
        "keep_last": 4
      }
    },
    {
      "detector": "pan",
      "method": "tokenize",
      "options": {
        "reversible": true,
        "format_preserving": true
      }
    }
  ]
}'

Response · 200

{
  "request_id": "req_6bc1f0",
  "content": "SSN ***-**-6789, card tok_4f21_8830_2201_9c40.",
  "token_map_id": "tmap_90a1",
  "transformations_applied": 2
}
POST/v3/dlp/restore

Restore (re-identify) content

Reverses reversible tokenization using the token map produced by a transform. Requires the restore scope and is fully audited.

Scope: dlp:restore

Body parameters

FieldTypeReq.Description
contentstringyesPreviously tokenized text.
token_map_idstringyesToken map returned by transform.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/restore" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "card tok_4f21_8830_2201_9c40.",
  "token_map_id": "tmap_90a1"
}'

Response · 200

{
  "request_id": "req_7cd2a1",
  "content": "card 4111 1111 1111 1111."
}
POST/v3/dlp/redact-image

Redact an image

Detects sensitive text in an image attachment (OCR + detectors) and returns the image with matching regions blacked out.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
image_base64stringyesBase64-encoded image.
detectorsstring[]Detector ids to redact. Defaults to your active profile.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/redact-image" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image_base64": "<base64>",
  "detectors": [
    "us_ssn",
    "pan"
  ]
}'

Response · 200

{
  "request_id": "req_88ea10",
  "redacted_image_base64": "<base64>",
  "regions_redacted": 2
}
POST/v3/dlp/detectors

Create a custom detector

Defines a custom detector from a regular expression, a dictionary/word list, or a small labeled sample set that trains a lightweight ML classifier.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
namestringyesDetector name.
typestringyesregex | dictionary | ml.
patternstringRegex, for type=regex.
termsstring[]Dictionary terms, for type=dictionary.
samplesstring[]Labeled examples, for type=ml.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/detectors" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Patient ID",
  "type": "regex",
  "pattern": "PT-\\d{8}"
}'

Response · 200

{
  "detector_id": "det_11c9",
  "name": "Patient ID",
  "type": "regex",
  "status": "active"
}
POST/v3/dlp/scan-profiles

Create a scan profile

A reusable inspection configuration — the detectors to run, minimum confidence, and the action to recommend. Attach a profile to sends, monitors, and jobs instead of repeating config.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
namestringyesProfile name.
detectorsstring[]yesDetector ids to include.
min_confidencestringlow | medium | high.
on_matchstringRecommended action: allow | encrypt | quarantine | block.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/scan-profiles" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "PHI outbound",
  "detectors": [
    "us_ssn",
    "mrn",
    "date_of_birth"
  ],
  "min_confidence": "medium",
  "on_match": "encrypt"
}'

Response · 200

{
  "profile_id": "prof_hipaa",
  "name": "PHI outbound",
  "detector_count": 3
}
POST/v3/dlp/transform-profiles

Create a transform profile

A reusable de-identification configuration applied by monitors, jobs, or on send.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
namestringyesProfile name.
transformationsobject[]yesPer-detector rules [{ detector, method, options }].

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/transform-profiles" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Mask PHI",
  "transformations": [
    {
      "detector": "us_ssn",
      "method": "mask",
      "options": {
        "keep_last": 4
      }
    }
  ]
}'

Response · 200

{
  "profile_id": "tprof_22a0",
  "name": "Mask PHI"
}
POST/v3/dlp/monitors

Create a monitor

Continuously (or on a schedule) scans a target — a mailbox, org unit, or storage connection — with a scan profile, and takes an action on findings. The mail-native evolution of scheduled DLP scans.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
namestringyesMonitor name.
targetobjectyesWhat to watch { type: mailbox|ou|connection, id }.
profile_idstringyesScan profile to apply.
schedulestringcontinuous (default) | hourly | daily.
actionstringnotify | quarantine | transform.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/monitors" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Watch Clinical OU",
  "target": {
    "type": "ou",
    "id": "ou_31b2"
  },
  "profile_id": "prof_hipaa",
  "schedule": "continuous",
  "action": "quarantine"
}'

Response · 200

{
  "monitor_id": "mon_5a7c",
  "status": "active",
  "schedule": "continuous"
}
POST/v3/dlp/jobs

Create a scan job

Starts an asynchronous bulk scan of a large data source (an entire mailbox, an OU, or a connected store) and returns a job you can poll. Also used for risk analysis over a dataset.

Scope: dlp:write

Body parameters

FieldTypeReq.Description
targetobjectyesSource to scan { type, id }.
profile_idstringyesScan profile to apply.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/jobs" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "target": {
    "type": "mailbox",
    "id": "mbx_3aa91b7c"
  },
  "profile_id": "prof_hipaa"
}'

Response · 200

{
  "job_id": "job_7f0c",
  "status": "running",
  "created_at": "2026-07-02T14:03:00Z"
}
GET/v3/dlp/jobs/{job_id}

Get a scan job

Returns a scan job's status and, when finished, a summary of findings by detector and severity.

Scope: dlp:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/dlp/jobs/{job_id}" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "job_id": "job_7f0c",
  "status": "done",
  "items_scanned": 4210,
  "findings_summary": [
    {
      "detector": "us_ssn",
      "count": 34,
      "max_severity": "high"
    },
    {
      "detector": "pan",
      "count": 8,
      "max_severity": "high"
    }
  ]
}
POST/v3/dlp/discovery

Run data discovery

Profiles where sensitive data lives across the tenant — which mailboxes and OUs hold which categories, and at what volume — without you specifying targets. The evolved, mail-native replacement for manual data profiling.

Scope: dlp:read

Body parameters

FieldTypeReq.Description
scopeobjectLimit discovery { type: org|ou, id }. Defaults to the whole tenant.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/discovery" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "scope": {
    "type": "org",
    "id": "org_7f31aa"
  }
}'

Response · 200

{
  "discovery_id": "disc_31aa",
  "status": "running",
  "preview": [
    {
      "mailbox_id": "mbx_3aa91b7c",
      "categories": [
        "health",
        "identity"
      ],
      "risk": "high"
    }
  ]
}
POST/v3/dlp/risk

Analyze re-identification risk

Computes re-identification risk metrics (k-anonymity and l-diversity style) over a dataset of quasi-identifiers, to gauge whether de-identified data is safe to share.

Scope: dlp:read

Body parameters

FieldTypeReq.Description
dataset_idstringyesDataset to analyze.
quasi_identifiersstring[]yesColumns treated as quasi-identifiers.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/dlp/risk" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dataset_id": "ds_44b0",
  "quasi_identifiers": [
    "zip",
    "birth_year",
    "gender"
  ]
}'

Response · 200

{
  "request_id": "req_9a01",
  "k_anonymity": 12,
  "l_diversity": 4,
  "risk": "low"
}

Email & Mailbox Management

v3

Provision mailboxes, send and scan mail, and manage aliases, distribution lists, contacts, and calendars for your tenant.

POST/v3/mailboxes

Create a mailbox

Provisions a new mailbox for a user on your tenant, with an optional quota and initial aliases. Credentials are managed and never returned.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
emailstringyesPrimary address for the mailbox.
display_namestringyesDisplay name shown on outbound mail.
quota_mbintegerStorage quota in MB. Defaults to the plan limit.
aliasesstring[]Alias addresses to create with the mailbox.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dana@stonebridge.health",
  "display_name": "Dana Okafor",
  "quota_mb": 51200,
  "aliases": [
    "d.okafor@stonebridge.health"
  ]
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "email": "dana@stonebridge.health",
  "status": "active",
  "quota_mb": 51200
}
GET/v3/mailboxes/{mailbox_id}/messages

List / scan inbox messages

Returns messages in a folder with DLP and threat flags surfaced inline. Cursor-paginated. Use ?unread=true to poll for new mail.

Scope: messages:read

Query parameters

FieldTypeReq.Description
folderstringMailbox folder. Defaults to inbox.
unreadbooleanReturn only unread messages.
limitintegerPage size, 1–200. Defaults to 50.
cursorstringPagination cursor from a prior response.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/mailboxes/{mailbox_id}/messages?folder=<folder>&unread=<unread>&limit=<limit>&cursor=<cursor>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "messages": [
    {
      "id": "msg_a11c92",
      "from": "vendor@example.com",
      "subject": "Invoice #4021",
      "received_at": "2026-07-02T13:40:00Z",
      "unread": true,
      "has_attachments": true,
      "dlp_flags": [
        "pci"
      ],
      "threat_verdict": "clean"
    }
  ],
  "next_cursor": "eyJvZmZzZXQiOjUwfQ"
}
POST/v3/mailboxes/{mailbox_id}/messages

Send an email

Sends a message from the mailbox. Outbound content passes through the tenant DLP policy before delivery.

Scope: messages:write

Body parameters

FieldTypeReq.Description
tostring[]yesRecipient addresses.
ccstring[]Carbon-copy addresses.
subjectstringyesMessage subject.
body_htmlstringyesHTML body.
attachmentsobject[]Attachments [{ filename, content_base64 }].

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/messages" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": [
    "ops@partner.com"
  ],
  "subject": "Kickoff notes",
  "body_html": "<p>Notes attached.</p>"
}'

Response · 200

{
  "message_id": "msg_5f0b2210",
  "status": "queued",
  "dlp_action": "allow"
}
POST/v3/messages/scan

Scan a message

Scans a raw message or an existing message id for threats (phishing, malware) and DLP matches, returning a verdict without delivering.

Scope: messages:read

Body parameters

FieldTypeReq.Description
message_idstringExisting message id to scan.
rawstringRaw RFC 5322 message to scan (base64).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/messages/scan" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "message_id": "msg_a11c92"
}'

Response · 200

{
  "request_id": "req_77bd01a2",
  "verdict": "clean",
  "threats": [],
  "dlp_matches": [
    {
      "lexicon": "pci",
      "type": "card_number",
      "count": 1,
      "severity": "high"
    }
  ]
}
POST/v3/mailboxes/{mailbox_id}/aliases

Add an alias

Adds an additional receiving address that delivers into the mailbox.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
aliasstringyesAlias address to add.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/aliases" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "alias": "sales@stonebridge.health"
}'

Response · 200

{
  "alias_id": "als_66a0",
  "alias": "sales@stonebridge.health",
  "status": "active"
}
POST/v3/distribution-lists

Create a distribution list

Creates a group address that fans out to its members. Add or remove members with the members sub-resource.

Scope: directory:write

Body parameters

FieldTypeReq.Description
addressstringyesGroup address, e.g. team@domain.
namestringyesDisplay name for the list.
membersstring[]Initial member addresses.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/distribution-lists" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "address": "care-team@stonebridge.health",
  "name": "Care Team",
  "members": [
    "dana@stonebridge.health",
    "m.okafor@stonebridge.health"
  ]
}'

Response · 200

{
  "list_id": "dl_90ab12",
  "address": "care-team@stonebridge.health",
  "member_count": 2
}
POST/v3/contacts

Create a contact

Adds a contact to a shared or personal contact list on the tenant directory.

Scope: directory:write

Body parameters

FieldTypeReq.Description
namestringyesContact full name.
emailsstring[]Email addresses.
phonesstring[]Phone numbers.
list_idstringContact list to add to.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/contacts" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Jordan Reyes",
  "emails": [
    "jordan@partner.com"
  ],
  "phones": [
    "+13055550188"
  ]
}'

Response · 200

{
  "contact_id": "con_4b7e",
  "name": "Jordan Reyes"
}
POST/v3/mailboxes/{mailbox_id}/calendar/events

Create a calendar event

Creates a calendar event on the mailbox and sends invites to attendees.

Scope: calendar:write

Body parameters

FieldTypeReq.Description
titlestringyesEvent title.
startstringyesISO 8601 start time.
endstringyesISO 8601 end time.
attendeesstring[]Attendee email addresses.
locationstringFree-text or conferencing location.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/calendar/events" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Quarterly review",
  "start": "2026-07-10T15:00:00Z",
  "end": "2026-07-10T16:00:00Z",
  "attendees": [
    "ops@partner.com"
  ]
}'

Response · 200

{
  "event_id": "evt_1c0d",
  "ical_uid": "1c0d@stonebridge.health",
  "status": "confirmed"
}
GET/v3/mailboxes

List mailboxes

Lists mailboxes on the organization, filterable by org unit and status. Cursor-paginated.

Scope: mailboxes:read

Query parameters

FieldTypeReq.Description
ou_idstringRestrict to an organizational unit.
statusstringactive | suspended.
cursorstringPagination cursor.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/mailboxes?ou_id=<ou_id>&status=<status>&cursor=<cursor>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "mailboxes": [
    {
      "mailbox_id": "mbx_3aa91b7c",
      "email": "dana@stonebridge.health",
      "status": "active",
      "ou_id": "ou_31b2"
    }
  ],
  "next_cursor": null
}
POST/v3/mailboxes/{mailbox_id}/suspend

Suspend a mailbox

Suspends a mailbox — blocks sign-in and mail flow without deleting data. Reverse with /reactivate.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
reasonstringAudit reason for suspension.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/suspend" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Offboarding"
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "status": "suspended"
}
POST/v3/mailboxes/{mailbox_id}/reset-password

Reset a mailbox password

Forces a password reset and optionally signs the user out of all sessions.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
require_changebooleanRequire the user to change it at next sign-in.
revoke_sessionsbooleanSign out all active sessions.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/reset-password" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "require_change": true,
  "revoke_sessions": true
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "status": "reset",
  "temporary_password": "shown once"
}
PUT/v3/mailboxes/{mailbox_id}/forwarding

Configure forwarding

Sets automatic forwarding for a mailbox, with an option to keep or discard the local copy.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
forward_tostringyesDestination address.
keep_copybooleanRetain a copy in the mailbox (default true).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PUT "$BASE_URL/v3/mailboxes/{mailbox_id}/forwarding" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "forward_to": "team@stonebridge.health",
  "keep_copy": true
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "forward_to": "team@stonebridge.health",
  "keep_copy": true
}
POST/v3/mailboxes/{mailbox_id}/send-as

Add a send-as address

Authorizes the mailbox to send mail as another verified address (e.g. a shared or group address).

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
addressstringyesVerified address to send as.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/send-as" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "address": "billing@stonebridge.health"
}'

Response · 200

{
  "send_as_id": "sa_71a0",
  "address": "billing@stonebridge.health",
  "verified": true
}
POST/v3/mailboxes/{mailbox_id}/delegates

Add a delegate

Grants another user delegated access to read and send on behalf of this mailbox.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
delegate_emailstringyesUser to grant delegation to.
accessstringread | send | full.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/delegates" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "delegate_email": "assistant@stonebridge.health",
  "access": "full"
}'

Response · 200

{
  "delegate_id": "dg_8802",
  "delegate_email": "assistant@stonebridge.health",
  "access": "full"
}
POST/v3/mailboxes/{mailbox_id}/filters

Create a mail filter

Creates a server-side rule that labels, archives, forwards, or deletes matching messages.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
criteriaobjectMatch criteria { from, subject_contains, has_attachment }.
actionsobjectActions { label, archive, forward_to, delete }.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mailboxes/{mailbox_id}/filters" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "criteria": {
    "from": "alerts@vendor.com"
  },
  "actions": {
    "label": "Alerts",
    "archive": true
  }
}'

Response · 200

{
  "filter_id": "flt_44b1",
  "enabled": true
}
PUT/v3/mailboxes/{mailbox_id}/signature

Set an email signature

Sets the mailbox's HTML signature, optionally appended to all outbound mail.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
htmlstringyesSignature HTML.
append_allbooleanAppend to every outbound message.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PUT "$BASE_URL/v3/mailboxes/{mailbox_id}/signature" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "html": "<p>Dana Okafor — Stonebridge Health</p>",
  "append_all": true
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "status": "updated"
}
PUT/v3/mailboxes/{mailbox_id}/vacation

Set an auto-reply

Configures a vacation responder (out-of-office auto-reply) over a date range.

Scope: mailboxes:write

Body parameters

FieldTypeReq.Description
enabledbooleanyesTurn the responder on or off.
subjectstringAuto-reply subject.
body_htmlstringAuto-reply body.
startstringISO 8601 start.
endstringISO 8601 end.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PUT "$BASE_URL/v3/mailboxes/{mailbox_id}/vacation" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true,
  "subject": "Out of office",
  "body_html": "<p>Back Monday.</p>",
  "start": "2026-07-05",
  "end": "2026-07-08"
}'

Response · 200

{
  "mailbox_id": "mbx_3aa91b7c",
  "vacation": "enabled"
}
POST/v3/distribution-lists/{list_id}/members

Add a distribution-list member

Adds a member (user or nested list) to a distribution list.

Scope: directory:write

Body parameters

FieldTypeReq.Description
emailstringyesMember address to add.
rolestringmember | manager.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/distribution-lists/{list_id}/members" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "newhire@stonebridge.health",
  "role": "member"
}'

Response · 200

{
  "list_id": "dl_90ab12",
  "email": "newhire@stonebridge.health",
  "status": "added"
}
GET/v3/contacts

List contacts

Returns contacts in the directory, searchable by name or email. Cursor-paginated.

Scope: directory:read

Query parameters

FieldTypeReq.Description
list_idstringRestrict to a contact list.
querystringSearch by name or email.
cursorstringPagination cursor.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/contacts?list_id=<list_id>&query=<query>&cursor=<cursor>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "contacts": [
    {
      "contact_id": "con_4b7e",
      "name": "Jordan Reyes",
      "emails": [
        "jordan@partner.com"
      ],
      "list_id": "list_partners"
    }
  ],
  "next_cursor": null
}
POST/v3/contact-lists

Create a contact list

Creates a shared contact list to segment contacts for directory and distribution use.

Scope: directory:write

Body parameters

FieldTypeReq.Description
namestringyesList name.
sharedbooleanShare across the organization (default true).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/contact-lists" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Partners",
  "shared": true
}'

Response · 200

{
  "list_id": "list_partners",
  "name": "Partners",
  "shared": true
}
POST/v3/mail/routing-rules

Create a mail routing rule

Creates an org-wide inbound/outbound routing rule — reroute, add a compliance footer, or force TLS to a partner domain.

Scope: mail:write

Body parameters

FieldTypeReq.Description
directionstringyesinbound | outbound.
matchobjectMatch { sender_domain, recipient_domain }.
actionobjectAction { require_tls, reroute_to, add_footer }.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/mail/routing-rules" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "direction": "outbound",
  "match": {
    "recipient_domain": "partner.com"
  },
  "action": {
    "require_tls": true
  }
}'

Response · 200

{
  "rule_id": "rr_0c71",
  "direction": "outbound",
  "enabled": true
}
POST/v3/vault/retention

Create a retention policy

Defines how long mail is retained (or must be preserved) for an org unit — for compliance and eDiscovery.

Scope: vault:write

Body parameters

FieldTypeReq.Description
ou_idstringOrg unit the policy applies to. Omit for org-wide.
retain_daysintegeryesDays to retain before purge.
actionstringpurge | preserve.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/vault/retention" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ou_id": "ou_31b2",
  "retain_days": 2555,
  "action": "preserve"
}'

Response · 200

{
  "policy_id": "ret_9a11",
  "retain_days": 2555,
  "action": "preserve"
}
POST/v3/vault/holds

Place a legal hold

Places a litigation hold on one or more mailboxes so mail cannot be deleted until the hold is released.

Scope: vault:write

Body parameters

FieldTypeReq.Description
namestringyesMatter/hold name.
mailbox_idsstring[]yesMailboxes to hold.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/vault/holds" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Matter 2026-14",
  "mailbox_ids": [
    "mbx_3aa91b7c"
  ]
}'

Response · 200

{
  "hold_id": "hold_5f2a",
  "name": "Matter 2026-14",
  "mailbox_count": 1,
  "status": "active"
}
GET/v3/mailboxes/{mailbox_id}/devices

List mobile devices

Returns the mobile devices syncing a mailbox, for mobile device management (MDM).

Scope: devices:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/mailboxes/{mailbox_id}/devices" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "devices": [
    {
      "device_id": "dev_71aa",
      "model": "iPhone 16",
      "os": "iOS 20",
      "last_sync": "2026-07-02T09:00:00Z",
      "status": "approved"
    }
  ]
}
POST/v3/devices/{device_id}/wipe

Wipe a device

Issues a remote wipe (account-only or full) to a lost or offboarded device.

Scope: devices:write

Body parameters

FieldTypeReq.Description
typestringaccount | full.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/devices/{device_id}/wipe" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "account"
}'

Response · 200

{
  "device_id": "dev_71aa",
  "status": "wipe_requested"
}

Marketing & Campaigns

v2

Send deliverability-engineered email campaigns and transactional mail. Manage templates and audiences and pull engagement stats.

POST/v2/messages/transactional

Send a transactional email

Sends a single templated transactional message (receipt, verification, alert) over an authenticated, warmed sending domain.

Scope: marketing:write

Body parameters

FieldTypeReq.Description
template_idstringyesTemplate to render.
tostringyesRecipient address.
variablesobjectTemplate merge variables.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/messages/transactional" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "template_id": "tmpl_welcome",
  "to": "alex@example.com",
  "variables": {
    "first_name": "Alex"
  }
}'

Response · 200

{
  "message_id": "msg_tx_66a1",
  "status": "queued"
}
POST/v2/templates

Create a template

Creates a reusable email template with merge variables for campaigns and transactional sends.

Scope: marketing:write

Body parameters

FieldTypeReq.Description
namestringyesTemplate name.
subjectstringyesSubject line (supports variables).
body_htmlstringyesHTML body with {{variables}}.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/templates" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Welcome",
  "subject": "Welcome, {{first_name}}",
  "body_html": "<p>Hi {{first_name}}</p>"
}'

Response · 200

{
  "template_id": "tmpl_welcome",
  "name": "Welcome"
}
POST/v2/audiences

Create an audience

Creates a targetable audience from a contact list or filter, with consent status honored automatically.

Scope: marketing:write

Body parameters

FieldTypeReq.Description
namestringyesAudience name.
list_idstringSource contact list.
filterobjectOptional segment filter.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/audiences" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "FL patients",
  "list_id": "list_partners",
  "filter": {
    "state": "FL"
  }
}'

Response · 200

{
  "audience_id": "aud_31c0",
  "name": "FL patients",
  "size": 1840
}
POST/v2/campaigns

Create & send a campaign

Schedules or immediately sends an email campaign to an audience using a template. Suppression and consent are enforced.

Scope: marketing:write

Body parameters

FieldTypeReq.Description
namestringyesCampaign name.
audience_idstringyesAudience to send to.
template_idstringyesTemplate to send.
send_atstringISO 8601 schedule time. Omit to send now.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v2/campaigns" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "July update",
  "audience_id": "aud_31c0",
  "template_id": "tmpl_welcome",
  "send_at": "2026-07-05T13:00:00Z"
}'

Response · 200

{
  "campaign_id": "camp_9f2a",
  "status": "scheduled",
  "recipients": 1840
}
GET/v2/campaigns/{campaign_id}/stats

Get campaign stats

Returns engagement metrics for a campaign — delivered, opens, clicks, bounces, unsubscribes.

Scope: marketing:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v2/campaigns/{campaign_id}/stats" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "campaign_id": "camp_9f2a",
  "delivered": 1815,
  "opens": 902,
  "clicks": 311,
  "bounces": 25,
  "unsubscribes": 7
}

Webhooks

v3

Subscribe to real-time events across the platform. Every delivery is signed with an HMAC-SHA256 signature you verify with your endpoint's signing secret (see Webhooks & signatures above).

POST/v3/webhooks

Create a webhook subscription

Registers an HTTPS endpoint to receive events. Returns a signing secret used to verify the HMAC signature on every delivery.

Scope: webhooks:write

Body parameters

FieldTypeReq.Description
urlstringyesHTTPS endpoint to POST events to.
eventsstring[]yesEvent types to subscribe to (or ["*"] for all).
descriptionstringLabel for the subscription.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/webhooks" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/hooks/bastion",
  "events": [
    "message.delivered",
    "message.bounced",
    "dlp.match"
  ],
  "description": "Delivery + DLP notifications"
}'

Response · 200

{
  "webhook_id": "wh_71a0",
  "url": "https://example.com/hooks/bastion",
  "events": [
    "message.delivered",
    "message.bounced",
    "dlp.match"
  ],
  "signing_secret": "whsec_… (shown once)",
  "status": "active"
}
GET/v3/webhooks

List webhook subscriptions

Lists webhook subscriptions for the organization.

Scope: webhooks:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/webhooks" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "webhooks": [
    {
      "webhook_id": "wh_71a0",
      "url": "https://example.com/hooks/bastion",
      "status": "active"
    }
  ]
}
PATCH/v3/webhooks/{webhook_id}

Update a subscription

Changes the URL, subscribed events, or active status of a webhook.

Scope: webhooks:write

Body parameters

FieldTypeReq.Description
urlstringNew endpoint URL.
eventsstring[]New event list.
statusstringactive | paused.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PATCH "$BASE_URL/v3/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "events": [
    "message.delivered",
    "message.opened",
    "message.bounced"
  ]
}'

Response · 200

{
  "webhook_id": "wh_71a0",
  "events": [
    "message.delivered",
    "message.opened",
    "message.bounced"
  ]
}
POST/v3/webhooks/{webhook_id}/rotate-secret

Rotate the signing secret

Rotates the HMAC signing secret. The previous secret remains valid for a 24-hour overlap to allow zero-downtime rollout.

Scope: webhooks:write

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/webhooks/{webhook_id}/rotate-secret" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "webhook_id": "wh_71a0",
  "signing_secret": "whsec_new… (shown once)",
  "previous_valid_until": "2026-07-03T14:03:00Z"
}
GET/v3/webhooks/{webhook_id}/deliveries

List deliveries

Returns recent delivery attempts with response codes for debugging. Failed deliveries retry with exponential backoff.

Scope: webhooks:read

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/webhooks/{webhook_id}/deliveries" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "deliveries": [
    {
      "delivery_id": "whd_5521",
      "event": "message.delivered",
      "status_code": 200,
      "attempts": 1,
      "at": "2026-07-02T14:03:05Z"
    },
    {
      "delivery_id": "whd_5522",
      "event": "message.bounced",
      "status_code": 500,
      "attempts": 3,
      "at": "2026-07-02T14:04:10Z"
    }
  ]
}
POST/v3/webhooks/{webhook_id}/deliveries/{delivery_id}/replay

Replay a delivery

Re-sends a specific event to the endpoint — useful after fixing a downstream outage.

Scope: webhooks:write

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/webhooks/{webhook_id}/deliveries/{delivery_id}/replay" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "delivery_id": "whd_5522",
  "status": "requeued"
}

Leads Marketplace

v1

Search, purchase, and distribute compliant B2B/B2C leads with consent certificates and ping-post support.

POST/v1/leads/search

Search available leads

Finds purchasable leads matching your filters and returns an estimated per-lead price. Does not reserve or purchase.

Scope: leads:read

Body parameters

FieldTypeReq.Description
verticalstringyese.g. auto | health | mortgage | solar.
geoobjectFilter { state, postal_code }.
exclusivitystringshared (default) | exclusive.
max_age_daysintegerMaximum lead age in days.
quantityintegerNumber of leads to preview.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v1/leads/search" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "vertical": "health",
  "geo": {
    "state": "FL"
  },
  "exclusivity": "exclusive",
  "max_age_days": 1,
  "quantity": 25
}'

Response · 200

{
  "available": 312,
  "estimated_price": 42.5,
  "currency": "USD",
  "sample": [
    {
      "lead_id": "lead_88a1",
      "vertical": "health",
      "state": "FL",
      "age_days": 0
    }
  ]
}
POST/v1/leads/purchase

Purchase a lead

Buys a specific lead (or the best match for filters) and returns the full contact record plus a TrustedForm-style consent certificate URL.

Scope: leads:write

Body parameters

FieldTypeReq.Description
lead_idstringSpecific lead to buy. Omit to buy best match.
verticalstringRequired if lead_id is omitted.
exclusivitystringshared | exclusive.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v1/leads/purchase" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "lead_id": "lead_88a1",
  "exclusivity": "exclusive"
}'

Response · 200

{
  "lead_id": "lead_88a1",
  "price": 45,
  "contact": {
    "name": "Alex Rivera",
    "phone": "+13055550188",
    "email": "alex@example.com",
    "postal_code": "33101"
  },
  "consent_certificate_url": "https://certs.example/tf_88a1",
  "purchased_at": "2026-07-02T14:03:00Z"
}
POST/v1/leads/ping

Ping (ping-post)

Submits partial lead data to receive a bid before committing. Returns a ping_id to post the full record if accepted.

Scope: leads:write

Body parameters

FieldTypeReq.Description
verticalstringyesLead vertical.
partialobjectNon-PII partial data { postal_code, coverage }.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v1/leads/ping" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "vertical": "auto",
  "partial": {
    "postal_code": "33101",
    "coverage": "full"
  }
}'

Response · 200

{
  "ping_id": "ping_2a71",
  "accepted": true,
  "bid": 18,
  "ttl_seconds": 60
}
POST/v1/leads/post

Post (ping-post)

Posts the full lead record against an accepted ping to complete the sale.

Scope: leads:write

Body parameters

FieldTypeReq.Description
ping_idstringyesAccepted ping id.
fullobjectyesFull lead record including contact fields.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v1/leads/post" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ping_id": "ping_2a71",
  "full": {
    "name": "Alex Rivera",
    "phone": "+13055550188",
    "email": "alex@example.com"
  }
}'

Response · 200

{
  "status": "sold",
  "lead_id": "lead_9c02",
  "price": 18
}
POST/v1/leads/{lead_id}/return

Return an invalid lead

Submits a purchased lead for return within the return window. Approved returns are credited automatically.

Scope: leads:write

Body parameters

FieldTypeReq.Description
reasonstringyeswrong_number | duplicate | invalid_contact.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v1/leads/{lead_id}/return" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "wrong_number"
}'

Response · 200

{
  "lead_id": "lead_88a1",
  "status": "accepted",
  "refund": 45
}