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": "chat:read",
      "product": "Chat Integration",
      "grants": "Read bridged spaces, messages, and attachments."
    },
    {
      "scope": "chat:write",
      "product": "Chat Integration",
      "grants": "Create spaces, post attributed messages, and react."
    },
    {
      "scope": "chat:admin",
      "product": "Chat Integration",
      "grants": "Manage members, access maps, commands, and delegated grants."
    },
    {
      "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"
}

Chat Integration

v3

Bridge Bastion users into your workspace chat (Google Chat and compatible platforms). Create and manage spaces, post attributed messages with quoted replies, mirror reactions and attachments, enforce per-user space access, and receive slash-command interaction events at a signed webhook.

POST/v3/chat/spaces

Create a chat space

Creates a space on the connected chat workspace with the integration as a founding member — which is what lets Bastion post and read there. Optionally seed real workspace members and bridged Bastion users in one call.

Scope: chat:write

Body parameters

FieldTypeReq.Description
namestringyesDisplay name of the space.
typestringcollaboration (default) | announcement | dm.
threadedbooleanAllow in-thread replies from workspace members (default false).
membersstring[]Workspace member emails to add as real members.
bridged_usersstring[]Bastion user ids to route into the space virtually (not workspace members).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/spaces" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Project Falcon",
  "type": "collaboration",
  "threaded": false,
  "members": [
    "dana@stonebridge.health"
  ],
  "bridged_users": [
    "usr_7d21",
    "usr_90ab"
  ]
}'

Response · 200

{
  "space_id": "spc_4f21a0",
  "provider": "google_chat",
  "provider_space": "spaces/AAQAtjsc9v4",
  "name": "Project Falcon",
  "type": "collaboration",
  "member_count": 2,
  "bridged_user_count": 2,
  "created_at": "2026-07-02T14:03:00Z"
}
GET/v3/chat/spaces

List chat spaces

Lists bridged spaces. Pass as_user to return only the spaces that user's access grants allow — the same filter enforced on every read, send, and download.

Scope: chat:read

Query parameters

FieldTypeReq.Description
as_userstringRestrict to spaces this Bastion user may access.
typestringcollaboration | announcement | dm.
cursorstringPagination cursor.

Request

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

Response · 200

{
  "spaces": [
    {
      "space_id": "spc_4f21a0",
      "name": "Project Falcon",
      "type": "collaboration",
      "member_count": 2,
      "unread": true
    },
    {
      "space_id": "spc_88be10",
      "name": "Care Team",
      "type": "collaboration",
      "member_count": 5,
      "unread": false
    }
  ],
  "next_cursor": null
}
POST/v3/chat/spaces/{space_id}/messages

Post a message

Posts to the space on behalf of a Bastion user. The message is delivered by the integration with the user's name attributed (bold sender line) — bridged users have no workspace identity of their own. Pass reply_to to send a quoted reply: the new message carries a quote of the original rather than joining a thread.

Scope: chat:write

Body parameters

FieldTypeReq.Description
on_behalf_ofstringyesBastion user id the message is attributed to.
textstringyesMessage text.
reply_tostringMessage id to quote. Quoting a workspace human's message requires a delegated user grant (see POST /v3/chat/grants).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/spaces/{space_id}/messages" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "on_behalf_of": "usr_7d21",
  "text": "Statement batch is ready for review.",
  "reply_to": "chm_31c0aa"
}'

Response · 200

{
  "message_id": "chm_9f2a11",
  "space_id": "spc_4f21a0",
  "attributed_to": "usr_7d21",
  "posted_as": "integration",
  "quote": {
    "message_id": "chm_31c0aa",
    "type": "reply"
  },
  "created_at": "2026-07-02T14:05:12Z"
}
GET/v3/chat/spaces/{space_id}/messages

Read a space

Returns messages with senders resolved (bridged messages re-attributed to the originating Bastion user, workspace humans resolved via the directory), quoted-reply context, reaction summaries, and attachment metadata. Cursor-paginated; poll with since for new activity.

Scope: chat:read

Query parameters

FieldTypeReq.Description
as_userstringyesBastion user reading the space — access is enforced server-side.
sincestringReturn messages after this ISO 8601 time.
cursorstringPagination cursor.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/chat/spaces/{space_id}/messages?as_user=<as_user>&since=<since>&cursor=<cursor>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "messages": [
    {
      "message_id": "chm_31c0aa",
      "sender": {
        "type": "human",
        "name": "Dana Okafor",
        "email": "dana@stonebridge.health"
      },
      "text": "Can someone confirm the July batch?",
      "reactions": [
        {
          "emoji": "👍",
          "count": 2
        }
      ],
      "attachments": [
        {
          "attachment_id": "att_55a0",
          "filename": "batch.xlsx",
          "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          "downloadable": true
        }
      ],
      "created_at": "2026-07-02T14:01:40Z"
    },
    {
      "message_id": "chm_9f2a11",
      "sender": {
        "type": "bridged",
        "user_id": "usr_7d21",
        "name": "Jordan Reyes"
      },
      "text": "Statement batch is ready for review.",
      "reply_to": {
        "message_id": "chm_31c0aa",
        "sender": "Dana Okafor",
        "text": "Can someone confirm the July batch?"
      },
      "created_at": "2026-07-02T14:05:12Z"
    }
  ],
  "next_cursor": null
}
POST/v3/chat/messages/{message_id}/reactions

Add or remove a reaction

Toggles a reaction on behalf of a Bastion user. In attributed mode (default) the integration posts a quoted '<user> reacted 😂' reply, preserving per-user attribution. In native mode the reaction is placed as a real workspace emoji reaction via a delegated grant — note all bridged users then collapse into one workspace identity, so native counts can differ from Bastion's per-user counts.

Scope: chat:write

Body parameters

FieldTypeReq.Description
on_behalf_ofstringyesBastion user reacting.
emojistringyesEmoji to toggle, e.g. 😂.
modestringattributed (default) | native | both.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/messages/{message_id}/reactions" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "on_behalf_of": "usr_7d21",
  "emoji": "😂",
  "mode": "attributed"
}'

Response · 200

{
  "message_id": "chm_31c0aa",
  "emoji": "😂",
  "state": "added",
  "mode": "attributed",
  "counts": {
    "bastion": 2,
    "workspace": 1
  }
}
GET/v3/chat/messages/{message_id}/attachments/{attachment_id}

Download an attachment

Returns a short-lived download URL for a file shared in a space. Two permission layers apply: the Bastion access map (may this user read the space?) and the workspace itself (the integration's delegated grant must be a member of the space). Files hosted in the workspace's drive are metadata-only and reported as not downloadable.

Scope: chat:read

Query parameters

FieldTypeReq.Description
as_userstringyesBastion user requesting the file — access is enforced server-side.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X GET "$BASE_URL/v3/chat/messages/{message_id}/attachments/{attachment_id}?as_user=<as_user>" \
  -H "Authorization: Bearer $BASTION_API_KEY"

Response · 200

{
  "attachment_id": "att_55a0",
  "filename": "batch.xlsx",
  "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "size_bytes": 48210,
  "download_url": "https://files.tenant.example/att_55a0?sig=… (valid 5 minutes)"
}
POST/v3/chat/spaces/{space_id}/members

Add a member

Adds a participant to a space — a real workspace member (created on the workspace via the admin grant) or a bridged Bastion user (routed virtually, no workspace membership). Remove with DELETE on the same sub-resource.

Scope: chat:admin

Body parameters

FieldTypeReq.Description
emailstringWorkspace member email to add as a real member.
user_idstringBastion user id to bridge into the space instead.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/spaces/{space_id}/members" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "m.okafor@stonebridge.health"
}'

Response · 200

{
  "membership_id": "chmem_71a0",
  "space_id": "spc_4f21a0",
  "type": "workspace_member",
  "status": "joined"
}
PUT/v3/chat/spaces/{space_id}/access

Set space access

Replaces the set of Bastion users allowed to read, post, and download in a space. The access map is enforced server-side on every request — a client cannot reach a space by requesting it directly. Changes take effect immediately.

Scope: chat:admin

Body parameters

FieldTypeReq.Description
user_idsstring[]yesBastion users granted access to this space.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X PUT "$BASE_URL/v3/chat/spaces/{space_id}/access" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "user_ids": [
    "usr_7d21",
    "usr_90ab",
    "usr_31c0"
  ]
}'

Response · 200

{
  "space_id": "spc_4f21a0",
  "granted": 3,
  "revoked": 1
}
POST/v3/chat/dm

Open a DM space

Creates — or silently reuses — a private space connecting one workspace member and one bridged Bastion user. Spaces are created lazily per pair (avoiding an N×M explosion) and keyed on the pair, so repeat calls return the existing space. Workspace members can also open one themselves with the /dm slash command.

Scope: chat:write

Body parameters

FieldTypeReq.Description
member_emailstringyesWorkspace member side of the DM.
user_idstringyesBridged Bastion user side of the DM.
reusebooleanReturn the existing space for this pair if one exists (default true).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/dm" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "member_email": "dana@stonebridge.health",
  "user_id": "usr_7d21",
  "reuse": true
}'

Response · 200

{
  "space_id": "spc_dm_20b1",
  "created": false,
  "member": "dana@stonebridge.health",
  "bridged_user": "usr_7d21"
}
POST/v3/chat/commands

Register a slash command

Registers a slash command workspace members can invoke in any space with the integration. Interaction events — command invocations, dialog submissions, button clicks — are POSTed to your endpoint with a signed identity token; verify its signature, issuer, and audience (your exact endpoint URL) before trusting the payload. Respond with a dialog definition to collect input, or a message to reply inline.

Scope: chat:admin

Body parameters

FieldTypeReq.Description
commandstringyesCommand name, e.g. /dm.
descriptionstringHelp text shown in the command picker.
endpointstringyesHTTPS URL to receive interaction events. Also used as the button-action target in dialogs.
opens_dialogbooleanWhether invoking the command opens a dialog (default false).

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/commands" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "command": "/dm",
  "description": "Start a private chat with a Bastion user",
  "endpoint": "https://example.com/hooks/chat-events",
  "opens_dialog": true
}'

Response · 200

{
  "command_id": "cmd_10ff",
  "command": "/dm",
  "endpoint": "https://example.com/hooks/chat-events",
  "opens_dialog": true,
  "verification": {
    "issuer": "events.bastion-platform",
    "audience": "https://example.com/hooks/chat-events"
  },
  "status": "active"
}
POST/v3/chat/grants

Create a delegated user grant

Links a consented workspace user to the integration for the few operations the workspace only permits a real user to perform — quoting a human's message, placing native reactions, downloading human-uploaded files, and admin membership changes. Returns a consent URL for the user to approve the requested capabilities.

Scope: chat:admin

Body parameters

FieldTypeReq.Description
emailstringyesWorkspace user granting delegated access.
capabilitiesstring[]yesAny of: quote_human_messages | native_reactions | media_download | admin_memberships.

Request

# BASE_URL is tenant-specific — request yours from support.
curl -X POST "$BASE_URL/v3/chat/grants" \
  -H "Authorization: Bearer $BASTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "admin@stonebridge.health",
  "capabilities": [
    "native_reactions",
    "media_download",
    "admin_memberships"
  ]
}'

Response · 200

{
  "grant_id": "cgr_44e2",
  "email": "admin@stonebridge.health",
  "capabilities": [
    "native_reactions",
    "media_download",
    "admin_memberships"
  ],
  "status": "pending_consent",
  "consent_url": "https://consent.tenant.example/cgr_44e2"
}

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
}