Skip to main content
/
API

Butterfly Security REST API

Every public endpoint lives under butterflysecurity.org/api/*. Authenticate with an OAuth 2.0 bearer token issued by the authorization server (see the MCP docs) or a per-user Supabase session for in-dashboard calls.

REST27 endpointsOAuth 2.0RFC 8707 audience-boundOpenAPI 3.1

Authentication

All /api/* requests authenticate via an OAuth 2.0 bearer token issued by the Butterfly authorization server, or via a per-user session cookie when the request originates from butterflysecurity.org itself.

Discovery

OAuth metadata is published at /.well-known/oauth-authorization-server. Both Dynamic Client Registration and Client ID Metadata Document flows are supported.

Audience binding

Per RFC 8707 Resource Indicators, tokens must carry the audience https://butterflysecurity.org/api/. Tokens minted for any other resource will be rejected.

Rate limits

  • 120 req/min on all /api/* endpoints by default
  • 10 req/min on /api/backup/run and /api/restore/* mutations
  • 20 req/min on /api/auth/send-code and /api/auth/verify-code

Limits are per-IP and reset every 60 seconds. Exceeding a limit returns 429 Too Many Requests.

Authorization header

Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Endpoint reference

27endpoints grouped by resource. Permissions are enforced server-side against the calling user's team role.

Backups

List, inspect, start, and poll backup runs. A backup captures the full configuration of one connected identity provider into encrypted R2 storage.

GET/api/backup/listview backups

Paginated, filterable list of backups across every connection the calling team can see.

Query parameters

connectionIdstring (uuid)– Filter to one connection.
connectionIdsstring (csv of uuid)– Filter to several connections.
statusrunning | completed | failed
searchstring– Free-text over connection name, error, resource types.
fromISO 8601 timestamp
toISO 8601 timestamp
resourceTypesstring (csv)
minSizeinteger (bytes)
maxSizeinteger (bytes)
limitinteger– Default 100.
offsetinteger– Default 0.
includeStatsboolean– Returns aggregate counts when true.

Response shape

{ backups: Backup[], total: number, stats: { totalBackups, completedBackups, failedBackups, successRate, totalSize, resourceTypes[] } | null }
GET/api/backup/[id]view backups

Fetch one backup by UUID, including resource counts, storage key, and owning connection.

Response shape

{ backup: Backup }
DELETE/api/backup/[id]run backups

Delete a backup record and its R2 snapshot. Irreversible.

Response shape

{ ok: true }
GET/api/backup/statusview backups

Poll the lifecycle of an in-flight backup. Use after POST /backup/run.

Query parameters

backupIdstring (uuid)required

Response shape

{ status, progress_pct, progress_message, error_message?, completed_at? }
GET/api/backup/activeview backups

List backups currently running for the calling team.

Response shape

{ active: Backup[] }
POST/api/backup/runrun backups

Kick off a backup for one connection. Returns immediately with a backupId; poll /api/backup/status for progress.

Request body (JSON)

connectionIdstring (uuid)required
includestring[]– Resource categories. Omit for a full snapshot.
includeWorkflowsboolean– Okta only.
forceFullBackupboolean– Skip incremental logic.

Response shape

{ backupId, instanceId, status: 'started' }

Connections

Identity-provider connections (Okta, Okta Workflows, Auth0). Credentials are encrypted at rest and never returned by the API.

GET/api/connectionsmanage connections

List every connection on the calling team across all supported providers.

Response shape

{ connections: Connection[] }
POST/api/connections/createmanage connections

Create a new Okta connection from an OIN-installed API Service Integration. See /docs for other providers.

Request body (JSON)

org_urlstring (https URL)required
client_idstringrequired
client_secretstringrequired
org_namestring

Response shape

{ connection: Connection }
GET/api/connections/[id]manage connections

Fetch one connection (credentials redacted).

Response shape

{ connection: Connection }
PATCH/api/connections/[id]manage connections

Update a connection's metadata or credentials.

Request body (JSON)

org_namestring
client_idstring
client_secretstring

Response shape

{ connection: Connection }
DELETE/api/connections/[id]manage connections

Remove a connection. Existing backups remain available until retention expires.

Response shape

{ ok: true }
GET/api/connections/[id]/schedulemanage connections

Fetch the current scheduled-backup configuration for a connection.

Response shape

{ enabled, frequency, hour, day_of_week?, day_of_month?, timezone, incremental_enabled, full_backup_day }
PATCH/api/connections/[id]/schedulemanage connections

Update the scheduled-backup configuration.

Request body (JSON)

enabledboolean
frequencyhourly | daily | weekly | monthly
hourinteger 0-23
day_of_weekinteger 0-6– weekly only
day_of_monthinteger 1-28– monthly only
timezonestring (IANA)
incremental_enabledboolean
full_backup_dayinteger 0-6

Response shape

{ schedule: Schedule }

Diff

Structured comparison between two backups, or between a backup and the live identity provider.

POST/api/diffview backups

Compare two backups by ID. Returns categorized changes by resource type.

Request body (JSON)

fromBackupIdstring (uuid)required
toBackupIdstring (uuid)required
resourceTypesstring[]– Optional filter.

Response shape

{ changes: { resourceType, action: 'added' | 'modified' | 'removed', resource, severity }[], summary }
GET/api/diffview backups

Same as POST /api/diff, with parameters supplied as query string.

Query parameters

fromBackupIdstring (uuid)required
toBackupIdstring (uuid)required

Response shape

Same as POST.

Restore

Two-phase restore: dry-run preview, then execute. Restore writes to the live identity provider, so it requires the restore permission on your team role, and mode defaults to dry-run. You must pass mode explicitly to execute.

GET/api/restorerestore

Dry-run preview of a restore. Returns per-resource-type counts of what would be created, updated, or skipped. Never writes.

Query parameters

backupIdstring (uuid)required
resourceTypesstring (csv)

Response shape

{ preview: { resourceType, create, update, skip }[], sourceOrgUrl, collectedAt }
POST/api/restorerestore

Execute a restore against the live identity provider. Supports selective restore via resourceTypes and resourceIds.

Request body (JSON)

backupIdstring (uuid)required
modefull | selectiverequired
resourceTypesstring[]– Required when mode is selective.
resourceIdsstring[]– Optional, narrows selective restore.

Response shape

{ restoreId, status: 'started' }

Compliance

Evaluate a connection against one of six compliance frameworks.

POST/api/compliance/checkcompliance

Run a compliance check for a connection against SOC 2, HIPAA, PCI DSS, NIST, ISO 27001, or CIS Controls.

Request body (JSON)

connectionIdstring (uuid)required
frameworksoc2 | hipaa | pci_dss | nist | iso27001 | cis_controlsrequired

Response shape

{ score, findings: { control, status, severity, evidence }[], generatedAt }

Scores

Numeric resilience and restore-readiness scores out of 100, with the contributing factors.

GET/api/scores/resilienceview backups

Resilience score for one connection (0-100) with five weighted factors and actionable recommendations.

Query parameters

connectionIdstring (uuid)required

Response shape

{ score, factors: { name, weight, value, notes }[], recommendations: string[] }
GET/api/scores/readinessview backups

Restore-readiness score (0-100) across every connection on the team.

Response shape

{ score, perConnection: { connectionId, score, lastBackupAt, drift }[] }

Export

Generate downloadable reports and auditor-ready evidence bundles.

POST/api/export/reportexport

Generate a PDF report for a backup, a compliance check, or a date range. Returns a signed download URL.

Request body (JSON)

typebackup | compliance | activityrequired
backupIdstring (uuid)– Required when type is backup.
frameworkstring– Required when type is compliance.
fromISO 8601 timestamp– Required when type is activity.
toISO 8601 timestamp– Required when type is activity.

Response shape

{ url, expiresAt }
POST/api/export/audit-packexport

Bundle backup history, restore drills, and compliance evidence into a single ZIP for auditors.

Request body (JSON)

fromISO 8601 timestamprequired
toISO 8601 timestamprequired
connectionIdsstring[]

Response shape

ZIP file (application/zip).

Notifications

Read in-app notifications for the calling user.

GET/api/notificationsview notifications

List notifications for the calling user, newest first.

Query parameters

limitinteger– Default 50.
unreadOnlyboolean

Response shape

{ notifications: Notification[] }

Resource timeline

Per-resource change history derived from sequential backups.

GET/api/resource-timelineview backups

Reconstruct the change history for one resource (user, group, app, policy) across the last N backups.

Query parameters

connectionIdstring (uuid)required
resourceTypestringrequired
resourceIdstringrequired
limitinteger– Default 20 backups.

Response shape

{ events: { backupId, timestamp, change: 'created' | 'modified' | 'deleted', diff }[] }

Newsletter

Public subscription endpoints. CAN-SPAM compliant.

POST/api/newsletter/subscribepublic

Subscribe an email address to the Butterfly Security newsletter.

Request body (JSON)

emailstringrequired

Response shape

{ ok: true }
GET/api/newsletter/unsubscribepublic

One-click unsubscribe link target (also accepts POST).

Query parameters

tokenstringrequired

Response shape

302 redirect to /unsubscribed.

Health

Liveness probe for monitoring.

GET/api/healthpublic

Returns 200 when the worker is reachable.

Response shape

{ status: 'ok', uptime, version }

Errors

Errors return a JSON envelope with a human-readable error string and, where applicable, a machinecode:

{
  "error": "Plan limit reached: upgrade to run additional backups",
  "code": "plan_limit_exceeded"
}

Standard codes

StatusMeaningWhen
401UnauthorizedMissing, expired, or invalid bearer token.
402Plan limit reachedFree or trial limits exhausted; upgrade required.
403ForbiddenRole lacks the required permission.
404Not foundResource does not exist or is not visible to the team.
429Rate limitedPer-IP rate-limit window exceeded.
500Internal errorUnhandled server-side failure; safe to retry idempotent GETs.

MCP client integration

Want to call these endpoints from a chat host instead of curl? The mcp-butterfly server wraps the most common API calls as Model Context Protocol tools with audience-bound JWT auth. See the MCP server docs for the full tool catalog and OAuth setup.

Add as a Claude custom connector in four steps:

1. Claude → Settings → Connectors → Add custom connector
2. URL: https://mcp.butterflysecurity.org/mcp
3. Complete the OAuth redirect to Butterfly
4. Start a chat – list_backups, diff_backups, run_backup, etc.

Code samples

Three ways to call the most-used endpoint, GET /api/backup/list.

curl

curl -sS https://butterflysecurity.org/api/backup/list?limit=10 \
  -H "Authorization: Bearer $BUTTERFLY_TOKEN" \
  -H "Accept: application/json"

Node (fetch)

const res = await fetch(
  "https://butterflysecurity.org/api/backup/list?limit=10",
  {
    headers: {
      Authorization: `Bearer ${process.env.BUTTERFLY_TOKEN}`,
      Accept: "application/json",
    },
  },
);
const { backups, total } = await res.json();

Python (requests)

import os, requests

r = requests.get(
    "https://butterflysecurity.org/api/backup/list",
    params={"limit": 10},
    headers={
        "Authorization": f"Bearer {os.environ['BUTTERFLY_TOKEN']}",
        "Accept": "application/json",
    },
    timeout=30,
)
r.raise_for_status()
data = r.json()

Bug, missing endpoint, or feature request? support@butterflysecurity.org