API Documentation
Everything you need to integrate Brio into your workflow. Generate reports, manage consultants, configure white-label branding, and track usage programmatically.
v4 — April 3, 2026
Authentication
All requests require an API key passed in the Authorization header:
Authorization: Bearer brio_sk_live_abc123...API keys are available on all paid plans. Each account has one API key. Generate, view, and regenerate from the Brio Dashboard (Account → API Keys). Key management is dashboard-only — keys cannot be managed via the API.
The key is shown once on creation. Brio stores a SHA-256 hash — the plaintext cannot be retrieved later.
Base URL
https://app.runbrio.com/api/v1Consultants
Consultants appear as the authored strategist on generated reports. Each account supports up to 5 consultants.
List Consultants
GET/consultants
{
"consultants": [
{
"id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"name": "Sarah Chen",
"title": "Senior Digital Strategist",
"avatar_url": "https://app.runbrio.com/storage/avatars/d4e5f6a7.jpg",
"cta_url": "https://calendly.com/sarah-chen",
"is_default": true,
"created_at": "2026-03-15T10:00:00Z"
}
]
}Create Consultant
POST/consultants
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Full name (1–60 characters) |
title | string | Yes | Job title (1–80 characters) |
photo | base64 | No | Headshot (JPEG or PNG, max 2 MB). Returned as avatar_url in responses |
cta_url | string | No | Link for the report's closing CTA button (Calendly, booking page, or any URL) |
is_default | boolean | No | Set as account default consultant. Only one default allowed per account |
Returns 409 if the account already has 5 consultants.
Update Consultant
PATCH/consultants/:id
Same fields as create. Only include fields you want to change.
Delete Consultant
DELETE/consultants/:id
Returns 400 if the consultant is set as the account default.
White Label
Manage your agency's branding applied to all generated reports. Every account has one white-label configuration, created automatically on signup.
Get White Label Config
GET/white-label
{
"agency_name": "Apex Digital",
"agency_website": "https://apexdigital.com",
"logo_url": "https://app.runbrio.com/storage/brand/x1y2z3/logo_light.png",
"accent_color": "#F59E0B",
"cta_button_text": "Book a Call",
"cta_url": "https://calendly.com/apex-digital"
}Update White Label Config
PATCH/white-label
Only include fields you want to change.
| Field | Type | Description |
|---|---|---|
agency_name | string | Agency name (max 80 characters) |
agency_website | string | Agency website URL, shown in report footers |
logo | base64 | Agency logo (PNG, SVG, or WebP, max 2 MB). Transparent background recommended. Returned as logo_url in responses. Pass null to remove |
accent_color | string | Brand accent color (hex, e.g. #F59E0B). Used for progress bars, highlights, and footer links. Pass null to reset |
cta_button_text | string | Button label on the report's closing CTA page |
cta_url | string | Button link URL on the closing CTA page |
Reports
List Reports
GET/reports
Returns a paginated list of reports on your account.
Query Parameters
| Param | Type | Description |
|---|---|---|
status | string | Filter by status: pending, processing, complete, failed |
since | ISO 8601 | Only reports created after this timestamp |
limit | integer | Results per page (default: 20, max: 100) |
cursor | string | Pagination cursor from previous response |
Example Response
{
"reports": [
{
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "complete",
"url": "https://example.com",
"business_model": "b2b-saas",
"consultant_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"created_at": "2026-03-28T14:30:00Z",
"completed_at": "2026-03-28T14:40:12Z"
}
],
"has_more": true,
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi..."
}Create Report
POST/reports
Submit a website URL for analysis. Returns a report object with pending status. Report generation takes approximately 5 minutes.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The website URL to analyze (must be publicly accessible) |
consultant_id | string | Yes | Consultant to feature on report |
webhook_url | string | No | URL to receive a POST when the report completes or fails |
Example Request
curl -X POST https://app.runbrio.com/api/v1/reports \
-H "Authorization: Bearer brio_sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"consultant_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"webhook_url": "https://yourapp.com/webhooks/brio"
}'Example Response
{
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "pending",
"url": "https://example.com",
"created_at": "2026-03-28T14:30:00Z"
}If your account already has 3 reports generating, the new report is accepted and queued — it will begin processing automatically when a slot opens.
Deduplication: If you submit the same URL while a report for that URL is still pending or was completed within the last 24 hours, the existing report is returned instead of creating a duplicate.
Get Report
GET/reports/:id
Response — Processing
{
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "processing",
"url": "https://example.com",
"created_at": "2026-03-28T14:30:00Z",
"progress": 0.45
}The progress field (0.0–1.0) is derived from the number of completed report sections. Poll this endpoint to track generation progress.
Response — Complete
{
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "complete",
"url": "https://example.com",
"created_at": "2026-03-28T14:30:00Z",
"completed_at": "2026-03-28T14:40:12Z",
"business_model": "b2b-saas",
"consultant_id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"industry_vertical": "Technology",
"pdf_url": "https://app.runbrio.com/api/v1/reports/8f3a.../pdf",
"sections": [
{ "title": "Executive Snapshot & Digital Maturity Scorecard", "data_tier": "full" },
{ "title": "Technical & Infrastructure Health", "data_tier": "full" },
{ "title": "SEO & Organic Visibility", "data_tier": "degraded" }
]
}Each report generates an Executive Snapshot plus 10 strategy sections. The sections array lists all completed sections. Each section includes a data_tier field (full, degraded, or partial) indicating the data quality level achieved. The business_model field reflects the detected model (e.g. b2b-saas, b2c-ecommerce, b2c-services) which drives the strategic frameworks used throughout the report.
Response — Failed
{
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "failed",
"url": "https://example.com",
"created_at": "2026-03-28T14:30:00Z",
"error": "The provided URL could not be resolved"
}Download PDF
GET/reports/:id/pdf
Returns the generated PDF file. White-label branding is applied based on your configuration.
Delete Report
DELETE/reports/:id
Permanently deletes a report and its associated PDF. This action cannot be undone. Does not restore usage credits.
Returns 204 No Content on success.
Usage
Get Usage
GET/usage
Returns current billing period usage for your account.
{
"plan": "50 Reports / $299",
"billing_period": {
"start": "2026-03-01T00:00:00Z",
"end": "2026-03-31T23:59:59Z"
},
"reports_included": 50,
"reports_used": 37,
"reports_remaining": 13,
"overage_count": 0,
"overage_rate": "$10.00"
}Webhooks
If you provide a webhook_url when creating a report, Brio will send a POST request when the report completes or fails.
Webhook Payload — Success
{
"event": "report.completed",
"report": {
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "complete",
"url": "https://example.com",
"pdf_url": "https://app.runbrio.com/api/v1/reports/8f3a.../pdf"
}
}Webhook Payload — Failure
{
"event": "report.failed",
"report": {
"id": "8f3a1b2c-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
"status": "failed",
"url": "https://example.com",
"error": "Unable to crawl the provided URL"
}
}Webhook Security
All webhook payloads include an X-Brio-Signature header for verification.
The signature is an HMAC-SHA256 hex digest of the raw request body, using your webhook signing secret (available in the Dashboard alongside your API key).
Verifying the signature
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Retry policy: If your endpoint returns a non-2xx status or times out (10s), Brio retries up to 5 times with exponential backoff (1m, 5m, 30m, 2h, 8h). After all retries fail, the webhook is abandoned. You can always poll GET /reports/:id as a fallback.
Deduplication
When you submit a URL via POST /reports, Brio checks whether your account already has a non-failed report for that same URL created within the last 24 hours. If one exists, the existing report is returned (with its current status) instead of creating a duplicate. This applies per-organization and uses the normalized URL for matching.
Rate Limits & Concurrency
- API requests are rate-limited to 60 requests per minute per API key
- Report creation (
POST /reports) is additionally limited to 10 requests per minute and 50 per hour - Up to 3 reports generating concurrently per account (dashboard and API combined). Additional reports are accepted and queued automatically
- Reports count against your plan's monthly allocation
- Overage reports are billed at $10 per report
- Report generation typically completes in approximately 5 minutes
Rate-limited requests receive a 429 response with a Retry-After header.
Status Values
Reports move through four statuses during their lifecycle.
| Status | Description |
|---|---|
pending | Report queued for processing |
processing | Analysis in progress (scanning, classifying, generating, or rendering) |
complete | Report ready for download |
failed | Generation failed — see error field |
Errors
All errors return a consistent format:
{
"error": {
"code": "invalid_url",
"message": "The provided URL could not be resolved"
}
}| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | invalid_url | URL is malformed or unreachable |
| 400 | invalid_request | Missing or invalid request body fields |
| 400 | incomplete_profile | White-label profile is missing required fields (returned with a missing_fields array) |
| 401 | unauthorized | Missing or invalid API key |
| 402 | usage_exceeded | Plan limit reached, overage billing failed |
| 402 | overage_limit_reached | Monthly overage cap reached (2× plan allocation). Resets next billing cycle |
| 404 | not_found | Resource does not exist or belongs to another account |
| 409 | consultant_limit | Maximum 5 consultants reached |
| 422 | unreachable_url | URL is valid but the website could not be reached |
| 429 | rate_limited | Too many requests — retry after cooldown |
| 500 | internal_error | Something went wrong on our end |
| 503 | service_unavailable | Report generation is temporarily degraded — retry after Retry-After seconds |
Quick Start
# 1. Create a report
REPORT_ID=$(curl -s -X POST https://app.runbrio.com/api/v1/reports \
-H "Authorization: Bearer brio_sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "consultant_id": "YOUR_CONSULTANT_ID"}' | jq -r '.id')
# 2. Poll until complete (~5 minutes)
curl -s https://app.runbrio.com/api/v1/reports/$REPORT_ID \
-H "Authorization: Bearer brio_sk_live_YOUR_KEY"
# 3. Download the PDF
curl -o report.pdf https://app.runbrio.com/api/v1/reports/$REPORT_ID/pdf \
-H "Authorization: Bearer brio_sk_live_YOUR_KEY"Changelog
v5 (April 7, 2026) — Trimmed white-label and consultant endpoints to only fields that render in the PDF. Removed: social links, gradients, testimonials, secondary colors, agency phone/tagline/email, CTA headline/body, consultant email/phone/linkedin/booking.
v4 (April 3, 2026) — Full field surface documented for white-label and consultant endpoints. Replaced idempotency with URL-based deduplication. Added report creation rate limits, error codes, and data tier to section responses.
v3 (April 3, 2026) — Tightened API surface to match production capabilities.
v2 (March 30, 2026) — Expanded white-label and consultant endpoints. Rate limit and concurrency documentation.
v1 (March 28, 2026) — Initial draft.