REST API Reference

All MCP tools map 1:1 to REST endpoints. Use the REST API directly for CI/CD pipelines, non-MCP integrations, webhook handlers, server-side automation, or any HTTP client.

Base URL: https://api.briefgate.dev

All requests and responses use JSON (Content-Type: application/json). All timestamps are ISO 8601 in UTC.


Authentication

Include your API key in every request as a Bearer token:

Authorization: Bearer bg_live_xxxxx

Key prefixes

Prefix Environment Behavior
bg_live_ Production Sends real emails and SMS to clients.
bg_test_ Sandbox Routes email to a test inbox in the dashboard; SMS is dropped silently. Webhooks fire normally.

Manage API keys

POST   /v1/keys         Create a new key
GET    /v1/keys         List all keys
DELETE /v1/keys/:id     Revoke a key

Create a key:

bash
curl -X POST https://api.briefgate.dev/v1/keys \
  -H "Authorization: Bearer bg_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI/CD pipeline",
    "mode": "live",
    "scopes": ["intakes:read", "intakes:write"]
  }'

Available scopes: admin, intakes:read, intakes:write, secrets:read. The admin scope includes all others. Keys created via the API inherit a maximum of the creating key's scopes.


Error format

All errors return a JSON body:

json
{
  "error": "not_found",
  "message": "Intake in_8f3k not found or does not belong to this account.",
  "request_id": "req_abc123"
}

Include request_id when contacting support.

Error codes

HTTP status error value Meaning
400 invalid_request Malformed JSON or missing required field. Check message for details.
401 unauthorized Missing or invalid API key.
403 forbidden Key does not have the required scope for this operation.
404 not_found Resource does not exist or belongs to a different account.
409 conflict A resource with the same identifier already exists (e.g. duplicate idempotency_key within the expiry window). Returns the existing resource.
410 gone Resource existed but has been permanently deleted or consumed (e.g. a secret token after first reveal).
413 payload_too_large File upload exceeds the declared max_bytes constraint or the account's hard limit.
422 unprocessable Request is structurally valid but semantically invalid (e.g. min_count greater than max_count).
429 rate_limited Too many requests. Honour the Retry-After header (seconds) before retrying.
402 quota_exceeded Monthly intake or file-storage quota reached.
402 plan_required Feature is not available on the current plan.
500 internal_error Unexpected server error. Safe to retry with exponential backoff.

Intakes

POST /v1/intakes

Create a new intake and send the client their portal link.

Request headers

Header Description
Idempotency-Key Optional. A unique string (UUID recommended). If an intake was already created with this key, the original is returned (HTTP 200) instead of creating a duplicate.

Key request fields

Field Type Required Description
project_name string Yes Human-readable project title.
client.email string Yes Client email address.
client.name string No Client display name for greetings.
client.language string No BCP 47 language code (default: en).
items array Yes Item definitions. See item-types.md.
chase_schedule string No default, gentle, or aggressive.
due_date string No ISO 8601 date shown to the client.
send boolean No Set to false to create a draft without sending. Call POST /v1/intakes/:id/send to send later. Default: true.
retention object No How long to keep data after completion. { mode: "days"|"on_delivery", days?: number, anonymize?: boolean }. Default: purge 90 days after intake.completed. Use mode: "on_delivery" for intakes with credentials — contents are removed ~24 h after you collect results.

Chase schedules

Schedule Reminders
default T+2 days, T+5 days, T+9 days, then weekly
gentle T+3 days, T+8 days, then bi-weekly
aggressive T+1 day, T+3 days, T+5 days, then every other day

Response (HTTP 201)

json
{
  "intake_id": "in_8f3kQmR2",
  "portal_url": "https://app.briefgate.dev/portal/in_8f3kQmR2",
  "status": "sent",
  "items": [
    { "key": "logo",       "status": "pending" },
    { "key": "hero_copy",  "status": "pending" }
  ]
}

Complete curl example

bash
curl -X POST https://api.briefgate.dev/v1/intakes \
  -H "Authorization: Bearer bg_live_xxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bella-napoli-website-2024" \
  -d '{
    "project_name": "Bella Napoli — Website",
    "client": {
      "email": "[email protected]",
      "name": "Marco Esposito",
      "language": "en"
    },
    "items": [
      {
        "key": "logo",
        "label": "Restaurant logo",
        "help": "SVG or PNG, transparent background, minimum 512px.",
        "type": "image",
        "required": true,
        "constraints": {
          "formats": ["svg", "png"],
          "min_width": 512,
          "transparent_background": true
        }
      },
      {
        "key": "hero_copy",
        "label": "Hero section tagline",
        "type": "longtext",
        "required": true,
        "constraints": { "max_chars": 400 }
      },
      {
        "key": "opening_hours",
        "label": "Opening hours",
        "type": "structured",
        "required": true,
        "schema": {
          "type": "object",
          "required": ["mon_fri", "sat", "sun"],
          "properties": {
            "mon_fri": { "type": "string" },
            "sat":     { "type": "string" },
            "sun":     { "type": "string" }
          }
        }
      },
      {
        "key": "photos",
        "label": "Food and interior photos",
        "type": "file_list",
        "required": true,
        "constraints": {
          "formats": ["jpg", "png", "heic"],
          "min_count": 5,
          "max_count": 15
        }
      }
    ],
    "chase_schedule": "default",
    "due_date": "2024-04-01"
  }'

GET /v1/intakes

List intakes with optional filters.

Query parameters

Parameter Description
status Filter by status: draft, sent, in_progress, completed, archived.
client_email Filter to a specific client.
limit Results per page (default 20, max 100).
offset Pagination offset (default 0).

Response

json
{
  "total": 47,
  "intakes": [
    {
      "id": "in_8f3kQmR2",
      "projectName": "Bella Napoli — Website",
      "clientEmail": "[email protected]",
      "status": "in_progress",
      "createdAt": "2024-03-15T10:22:00Z"
    }
  ]
}

Note: The list response returns intake fields in camelCase (raw DB representation). Use GET /v1/intakes/:id/status for a fully formatted status object.


GET /v1/intakes/:id

Retrieve the full intake definition including all item definitions and metadata.


GET /v1/intakes/:id/status

Lightweight status endpoint. Returns item statuses, chase history, and progress without transferring file content or signed URLs.

Response

json
{
  "status": "in_progress",
  "due_date": "2024-04-01",
  "client_last_seen": "2024-03-16T14:05:33Z",
  "progress": {
    "submitted": 2,
    "total": 4,
    "outstanding": 2,
    "percent": 50
  },
  "items": [
    { "key": "logo",          "status": "approved",       "submitted_at": "2024-03-16T10:00:00Z", "label": "Restaurant logo" },
    { "key": "hero_copy",     "status": "needs_revision", "submitted_at": "2024-03-16T11:00:00Z", "label": "Hero section tagline" },
    { "key": "opening_hours", "status": "pending",        "submitted_at": null,                   "label": "Opening hours" },
    { "key": "photos",        "status": "submitted",      "submitted_at": "2024-03-16T12:00:00Z", "label": "Food and interior photos" }
  ],
  "chases": [
    { "channel": "email", "sent_at": "2024-03-17T09:00:00Z", "status": "sent",      "attempt_no": 1 },
    { "channel": "email", "sent_at": null,                   "status": "scheduled", "attempt_no": 2 }
  ]
}

GET /v1/intakes/:id/results

Returns typed results for approved items. File and image items include signed URLs (24-hour validity).

Query parameters

Parameter Type Description
only_new boolean Only return items approved since the last call to this endpoint.
include_pending boolean Also include items in submitted status (not yet approved).

POST /v1/intakes/:id/items

Add items to an existing intake. The client is notified by email.

Request body

json
{
  "items": [
    {
      "key": "favicon",
      "label": "Favicon",
      "type": "image",
      "required": true,
      "constraints": { "formats": ["png","svg"], "min_width": 32 }
    }
  ]
}

Response

json
{
  "intake_id": "in_8f3kQmR2",
  "items_added": [
    { "key": "favicon", "status": "pending" }
  ]
}

POST /v1/intakes/:id/revision

Request a revision on a specific item.

Request body

json
{
  "item_key": "logo",
  "note": "The logo appears blurry at 320px. Please re-export at minimum 512px, ideally as SVG."
}

Response

json
{
  "intake_id": "in_8f3kQmR2",
  "item_key": "logo",
  "status": "needs_revision",
  "client_notified": true
}

POST /v1/intakes/:id/chase

Manually trigger a chase message outside the automatic schedule.

Request body

json
{
  "channel": "sms"
}

channel is optional. Defaults to email. Use sms to escalate after emails have bounced or to reach the client through a different channel.


POST /v1/intakes/:id/send

Send a draft intake to the client. Only valid when status is draft.


DELETE /v1/intakes/:id

Permanently delete an intake and all associated uploaded files. This action is irreversible. Returns HTTP 204 on success.


Templates

Save an intake definition as a reusable template to avoid re-specifying items for recurring project types.

GET   /v1/templates        List all templates
POST  /v1/templates        Create a template from an item definition array

Create a template

bash
curl -X POST https://api.briefgate.dev/v1/templates \
  -H "Authorization: Bearer bg_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Restaurant Website",
    "items": [...]
  }'

Use the template ID in define_intake (or POST /v1/intakes) with "template_id": "tmpl_xyz" instead of specifying items manually.


Webhooks

BriefGate fires webhooks on intake state changes. All webhook payloads include intake_id, event, and occurred_at.

GET    /v1/webhooks              List configured endpoints
POST   /v1/webhooks              Register a new endpoint
DELETE /v1/webhooks/:id          Remove an endpoint
POST   /v1/webhooks/:id/test     Send a test event to the endpoint

Register a webhook

bash
curl -X POST https://api.briefgate.dev/v1/webhooks \
  -H "Authorization: Bearer bg_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/briefgate",
    "events": ["intake.complete", "item.submitted", "item.approved"],
    "secret": "whsec_your_signing_secret"
  }'

BriefGate signs each webhook request with an HMAC-SHA256 signature in the BriefGate-Signature header. Verify it using your secret before processing the payload.

Events

Event Fired when
intake.sent Intake email delivered to client.
intake.viewed Client opened the portal for the first time.
intake.complete All required items are approved.
item.submitted Client uploaded or filled in an item.
item.approved Item approved (explicitly or via 72h auto-approval).
item.revision_requested Agent called request_revision.
chase.sent A chase email or SMS was dispatched.

Account and billing

GET   /v1/account               Account info and current tier
PATCH /v1/account               Update account settings (name, default language, etc.)
GET   /v1/usage                 Current period usage against plan limits
GET   /v1/audit                 Paginated audit log of API and user actions
POST  /v1/billing/checkout      Create a Stripe Checkout session to upgrade
POST  /v1/billing/portal        Create a Stripe Customer Portal session to manage subscription

Usage response

json
{
  "period": "2024-03",
  "intakes": { "used": 12, "limit": 50 },
  "storage_bytes": { "used": 524288000, "limit": 5368709120 },
  "sms_messages": { "used": 3, "limit": 100 }
}

Public endpoints

These endpoints require no authentication and are designed for programmatic consumption.

Endpoint Description
GET /pricing.json Machine-readable pricing tiers, limits, and feature flags. Intended for agents to decide which plan to recommend or to check quota before creating intakes.
GET /v1/openapi.json OpenAPI 3.1 specification for the entire API. Import into Postman, generate an SDK, or pass to an agent for full API introspection.
GET /healthz Liveness probe. Returns HTTP 200 with {"status":"ok"} when the API process is running.
GET /readyz Readiness probe. Returns HTTP 200 only when the API process and all downstream dependencies (database, object storage, email provider) are healthy. Returns HTTP 503 during deployments or outages.