Skip to content

API Reference

Beaco exposes a JSON REST API for event ingestion, delivery operations, templates, analytics, and admin controls.

  • Base URL: https://beaco.michaelogunjimi.com/api/v1
  • Authentication: bearer access token or scoped X-API-Key, depending on the endpoint

For conceptual guides, see Events, Templates, and Delivery Pipeline.

Authentication

Beaco has two authentication planes:

CredentialUsed forHeader
Human access tokenOrganizations, projects, members, invitations, API-key management, and platform administrationAuthorization: Bearer TOKEN
Project API keyEvents, templates, notifications, delivery operations, and project observabilityX-API-Key: KEY

For example, a notification operation includes:

Bash
curl -H "X-API-Key: YOUR_API_KEY" ...

Project API keys are created under /projects/{project_id}/api-keys by an authorized human user. Each key carries explicit scopes such as events:write, templates:read, or notifications:read; a missing scope returns 403.

Events

POST /events

Create an event and enqueue notification fan-out.

  • Auth required: project API key with events:write
  • Response: 202 Accepted

Request body

FieldTypeRequiredDescription
event_typestringYesEvent name, e.g. order.shipped
recipientsarrayYesRecipient list
payloadobjectYesEvent payload
priorityenumNohigh, medium, low
template_idUUIDOne of threeTemplate to render
template_namestringOne of threeTemplate name to render
inlineobjectOne of threeRendered email, see below
attachmentsarrayNoEmail attachments, see below
idempotency_keystringNoDeduplication key
metadataobjectNoOptional metadata

Exactly one of template_id, template_name, or inline is required. Inline content is size validated and delivered without Jinja processing.

inline object:

FieldTypeRequiredDescription
htmlstringYesRendered HTML body
subjectstringNoPlain-text subject (max 500)
textstringNoPlain-text alternative; derived from html when absent
from_localstringNoSender name before the @: ^[a-z0-9._+-]+$, max 64
from_namestringNoSender display name, plain text, max 100
reply_tostringNoReply-To address; any valid email address

The sender domain always comes from the server's configured address, so from_local and from_name can never move an email to another domain. When none is set, the default sender is used. Example: "inline": {"subject": "Order confirmed", "html": "<p>Thanks</p>", "from_local": "orders", "from_name": "Winwell Orders", "reply_to": "[email protected]"} sends as Winwell Orders <orders@your-verified-domain>.

attachments[] object (email only, up to 10 per event):

FieldTypeRequiredDescription
filenamestringYesName shown to the recipient, 1-255 chars, no slashes or controls
urlstringYeshttp(s) URL of the file, max 2048
size_bytesintegerYesDeclared size in bytes; the declared total may not exceed 30 MB

Beaco does not store attachments. The file is downloaded from your url each time the email is sent: by Resend, or by Beaco's worker when SMTP is the provider. The URL must therefore stay reachable until delivery succeeds, including retries; a short-lived signed URL can expire mid-retry. Beaco's own download only connects to publicly routable addresses, so URLs on private or internal networks (localhost, 10.x, 192.168.x, link-local) are refused, redirects are not followed, and a 4xx response fails the delivery permanently. size_bytes is declared by the caller and used only to reject oversized emails early (Resend allows 40 MB per email after Base64 encoding, so 30 MB of files); Beaco never fetches the URL when you publish, and the real size is enforced at send time. POST /scheduled-events does not accept attachments; the field is ignored there.

recipients[] object:

FieldTypeRequiredDescription
channelsarrayYesOne or more of email, sms, webhook
emailstringConditionalRequired if channel includes email
phonestringConditionalRequired if channel includes sms
webhook_urlstringConditionalRequired if channel includes webhook
user_idstringNoYour internal user id
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/events \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{
    "event_type": "order.shipped",
    "recipients": [{
      "channels": ["email", "sms"],
      "email": "[email protected]",
      "phone": "+15551234567",
      "user_id": "usr_123"
    }],
    "payload": {"order_id": "ord_991", "tracking_number": "1Z123"},
    "priority": "high",
    "template_id": "799524b8-fdc7-4f56-8a07-3b00bbc377af",
    "idempotency_key": "ship-ord_991-v1",
    "metadata": {"source": "orders-service"}
  }'
JSON
{
  "id": "3d434cf3-2f63-4a82-aae4-fbead6877445",
  "event_type": "order.shipped",
  "status": "accepted",
  "priority": "high",
  "recipient_count": 1,
  "has_failures": false,
  "idempotency_key": "ship-ord_991-v1",
  "created_at": "2026-04-17T12:15:32Z",
  "updated_at": "2026-04-17T12:15:32Z"
}

Example with attachments (email delivery only):

Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/events \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{
    "event_type": "invoice.issued",
    "recipients": [{"channels": ["email"], "email": "[email protected]"}],
    "inline": {"subject": "Your invoice", "html": "<p>Invoice attached.</p>"},
    "attachments": [
      {
        "filename": "invoice-1042.pdf",
        "url": "https://files.example.com/invoices/1042.pdf",
        "size_bytes": 48213
      }
    ]
  }'

A request is rejected with 422 when there are more than 10 attachments, a url is not http(s), a filename contains a slash or control character, size_bytes is not positive, or the declared sizes total more than 30 MB.

POST /events/batch

Create multiple events atomically.

  • Auth required: scoped project API key
  • Response: 202 Accepted
  • Limit: max 50 events

Request body

FieldTypeRequiredDescription
eventsarrayYesList of event objects (same shape as POST /events)
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/events/batch \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{
    "events": [
      {"event_type":"invoice.created","template_name":"invoice-created","recipients":[{"channels":["email"],"email":"[email protected]"}],"payload":{"invoice_id":"inv_100"}},
      {"event_type":"invoice.created","template_name":"invoice-created","recipients":[{"channels":["email"],"email":"[email protected]"}],"payload":{"invoice_id":"inv_101"}}
    ]
  }'
JSON
{
  "batch_id": "09f33df8-0499-4db6-8adf-c235f34ca26f",
  "status": "accepted",
  "events_created": 2,
  "notifications_created": 2
}

GET /events

List events.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
statusstringEvent status
prioritystringhigh, medium, low
event_typestringExact event type filter
date_fromdatetimeLower bound
date_todatetimeUpper bound
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/events?page=1&per_page=2&priority=high" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "3d434cf3-2f63-4a82-aae4-fbead6877445",
      "event_type": "order.shipped",
      "priority": "high",
      "status": "processing",
      "created_at": "2026-04-17T12:15:32Z"
    },
    {
      "id": "8dc13b38-19cc-40ef-9f85-8d3f402024f1",
      "event_type": "password.reset",
      "priority": "high",
      "status": "completed",
      "created_at": "2026-04-17T11:49:02Z"
    }
  ],
  "total": 42,
  "page": 1,
  "per_page": 2,
  "total_pages": 21
}

GET /events/{id}

Get event details with related notifications.

  • Auth required: scoped project API key
Bash
curl -X GET https://beaco.michaelogunjimi.com/api/v1/events/3d434cf3-2f63-4a82-aae4-fbead6877445 \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "id": "3d434cf3-2f63-4a82-aae4-fbead6877445",
  "event_type": "order.shipped",
  "status": "processing",
  "priority": "high",
  "payload": { "order_id": "ord_991" },
  "notifications": [
    { "id": "f8ce0a5f-9d95-4483-95d7-bda38ab718e8", "channel": "email", "status": "delivered" },
    { "id": "5f2b7371-c2d2-464b-ac53-89b683b80f68", "channel": "sms", "status": "processing" }
  ]
}

Notifications

GET /notifications

List notifications.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
statusstringNotification status
channelstringemail, sms, webhook
date_fromdatetimeLower bound
date_todatetimeUpper bound
recipientstringRecipient filter
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/notifications?channel=email&status=delivered" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "f8ce0a5f-9d95-4483-95d7-bda38ab718e8",
      "event_id": "3d434cf3-2f63-4a82-aae4-fbead6877445",
      "channel": "email",
      "status": "delivered",
      "recipient": "[email protected]",
      "retry_count": 0
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

GET /notifications/{id}

Get notification detail with attempt logs.

  • Auth required: scoped project API key
Bash
curl -X GET https://beaco.michaelogunjimi.com/api/v1/notifications/f8ce0a5f-9d95-4483-95d7-bda38ab718e8 \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "id": "f8ce0a5f-9d95-4483-95d7-bda38ab718e8",
  "channel": "email",
  "status": "delivered",
  "recipient": "[email protected]",
  "retry_count": 0,
  "notification_logs": [
    { "attempt": 1, "status": "processing", "timestamp": "2026-04-17T12:15:34Z" },
    {
      "attempt": 1,
      "status": "delivered",
      "provider_response": { "message_id": "re_2SgB..." },
      "timestamp": "2026-04-17T12:15:36Z"
    }
  ]
}

Templates

See Templates.

GET /templates

List templates.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
channelstringFilter by channel
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/templates?channel=email" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "799524b8-fdc7-4f56-8a07-3b00bbc377af",
      "name": "order_shipped_email",
      "channel": "email",
      "subject": "Your order {{ order_id }} has shipped",
      "variables": ["order_id", "tracking_number"]
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

POST /templates

Create template.

  • Auth required: scoped project API key

Request body

FieldTypeRequiredDescription
namestringYesTemplate name
channelstringYesemail, sms, webhook
subjectstringNoPlain-text email subject
bodystringYesTemplate body / email HTML
text_bodystringNoPlain-text email alternative
from_localstringNoEmail sender name before the @
from_namestringNoEmail sender display name
reply_tostringNoEmail Reply-To address
variablesarrayNoMust match auto-detected variables
on_missing_variableenumNoerror (default) or blank
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/templates \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{"name":"order_shipped_email","channel":"email","subject":"Order {{ order_id }} is on the way","body":"Hi {{ customer_name }}, track: {{ tracking_number }}","variables":["order_id","customer_name","tracking_number"]}'
JSON
{
  "id": "799524b8-fdc7-4f56-8a07-3b00bbc377af",
  "name": "order_shipped_email",
  "channel": "email",
  "detected_variables": ["customer_name", "order_id", "tracking_number"],
  "created_at": "2026-04-17T12:35:00Z"
}

PUT /templates/by-name/{name}

Create or update an active project template by name. Pass channel as a query parameter; it defaults to email. This endpoint is safe to call on every deployment.

POST /templates/import

Create an email template from plain HTML and sample values. Samples must appear exactly once in an HTML text node; attribute or duplicate matches return 422 instead of being guessed.

JSON
{
  "name": "order-confirmed",
  "subject": "Your order is confirmed",
  "html": "<h1>Thanks, Chidi</h1>",
  "variables": { "customer_name": "Chidi" }
}

GET /templates/{id}

Get template by id.

  • Auth required: scoped project API key
Bash
curl -X GET https://beaco.michaelogunjimi.com/api/v1/templates/799524b8-fdc7-4f56-8a07-3b00bbc377af \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "id": "799524b8-fdc7-4f56-8a07-3b00bbc377af",
  "name": "order_shipped_email",
  "channel": "email",
  "subject": "Order {{ order_id }} is on the way",
  "body": "Hi {{ customer_name }}, track: {{ tracking_number }}",
  "variables": ["order_id", "customer_name", "tracking_number"]
}

PUT /templates/{id}

Update template (partial payload allowed).

  • Auth required: scoped project API key

Request body

FieldTypeDescription
namestringUpdated name
subjectstringUpdated subject
bodystringUpdated body
variablesarrayUpdated variable list
Bash
curl -X PUT https://beaco.michaelogunjimi.com/api/v1/templates/799524b8-fdc7-4f56-8a07-3b00bbc377af \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{"subject":"Your order {{ order_id }} has shipped","body":"Hi {{ customer_name }}, your package is now in transit."}'
JSON
{
  "id": "799524b8-fdc7-4f56-8a07-3b00bbc377af",
  "name": "order_shipped_email",
  "channel": "email",
  "subject": "Your order {{ order_id }} has shipped",
  "updated_at": "2026-04-17T12:38:43Z"
}

DELETE /templates/{id}

Delete template.

  • Auth required: scoped project API key
  • Response: 204 No Content
Bash
curl -X DELETE https://beaco.michaelogunjimi.com/api/v1/templates/799524b8-fdc7-4f56-8a07-3b00bbc377af \
  -H "X-API-Key: PROJECT_KEY"

POST /templates/{id}/preview

Render template using variables.

  • Auth required: scoped project API key

Request body

FieldTypeRequiredDescription
variablesobjectYesRuntime variable values
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/templates/799524b8-fdc7-4f56-8a07-3b00bbc377af/preview \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{"variables":{"order_id":"ord_991","customer_name":"Alex","tracking_number":"1Z123"}}'
JSON
{
  "subject": "Your order ord_991 has shipped",
  "html": "<p>Hi Alex, your package is now in transit.</p>",
  "text": "Hi Alex, your package is now in transit.",
  "variables_used": ["customer_name", "order_id", "tracking_number"],
  "missing_variables": [],
  "body": "<p>Hi Alex, your package is now in transit.</p>"
}

Scheduled Events

Scheduled events are stored now and turned into regular events when scheduled_for arrives. A scheduler checks every minute, so delivery starts within about a minute of the requested time. If the platform was unable to dispatch an event for more than an hour after scheduled_for, it becomes expired and is not sent.

Lifecycle status: pending (waiting), dispatched (an event was created; event_id is set and you can follow it with GET /events/{id}), cancelled, failed (the content could not be delivered, for example the template was deleted after scheduling) and expired. processing is reserved and not currently used. For failed and expired events, failure_reason says why.

POST /scheduled-events

Schedule future event delivery.

  • Auth required: scoped project API key (scheduled_events:write)
  • Response: 201 Created

Request body

Takes the same fields as POST /events, except idempotency_key, plus scheduled_for. Exactly one of template_id, template_name, or inline is required, and attachments follow the same limits (up to 10 files, 30 MB declared in total).

FieldTypeRequiredDescription
event_typestringYesEvent name
recipientsarrayYesRecipient array
scheduled_fordatetimeYesFuture ISO 8601 datetime with a timezone
template_idUUIDOne of threeTemplate to render
template_namestringOne of threeTemplate name to render
inlineobjectOne of threeAlready-rendered email content
attachmentsarrayNoEmail attachments, see POST /events
payloadobjectNoEvent payload
metadataobjectNoFree-form metadata
prioritystringNohigh, medium (default), low

A request is rejected with 422 when it does not have exactly one content source, a recipient lacks the contact field for a requested channel, template_name does not exist, or the attachments break the limits. Templates are rendered at dispatch time, so edits made before scheduled_for apply.

Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/scheduled-events \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{"event_type":"renewal.reminder","recipients":[{"channels":["email"],"email":"[email protected]"}],"template_name":"renewal-reminder","payload":{"renewal_date":"2026-04-20"},"scheduled_for":"2026-04-19T09:00:00Z"}'
JSON
{
  "id": "39ac7bf3-c4f6-4a1f-ab1a-996f1fcf2d5d",
  "api_key_id": "5d0c5f0e-52c1-4b34-9a8e-1f3f4c9e7a10",
  "event_type": "renewal.reminder",
  "scheduled_for": "2026-04-19T09:00:00Z",
  "priority": "medium",
  "status": "pending",
  "event_id": null,
  "failure_reason": null,
  "created_at": "2026-04-18T12:00:00Z",
  "updated_at": "2026-04-18T12:00:00Z"
}

GET /scheduled-events

List scheduled events.

  • Auth required: scoped project API key (scheduled_events:read)

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
statusstringpending, processing, dispatched, cancelled, failed, or expired
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/scheduled-events?status=pending" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "39ac7bf3-c4f6-4a1f-ab1a-996f1fcf2d5d",
      "api_key_id": "5d0c5f0e-52c1-4b34-9a8e-1f3f4c9e7a10",
      "event_type": "renewal.reminder",
      "scheduled_for": "2026-04-19T09:00:00Z",
      "priority": "medium",
      "status": "pending",
      "event_id": null,
      "failure_reason": null,
      "created_at": "2026-04-18T12:00:00Z",
      "updated_at": "2026-04-18T12:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

DELETE /scheduled-events/{id}

Cancel scheduled event.

  • Auth required: scoped project API key (scheduled_events:write)
  • Response: 204 No Content; cancelling an already cancelled event is a no-op
  • Conflict: 409 when the event is no longer pending (it was dispatched, failed or expired)
Bash
curl -X DELETE https://beaco.michaelogunjimi.com/api/v1/scheduled-events/39ac7bf3-c4f6-4a1f-ab1a-996f1fcf2d5d \
  -H "X-API-Key: PROJECT_KEY"

Suppressions

GET /suppressions

List suppressions.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
channelstringChannel filter
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/suppressions?channel=email" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "90763e0e-e417-44a5-b0d4-ec9793fdf52f",
      "channel": "email",
      "recipient": "[email protected]",
      "reason": "hard_bounce",
      "source": "system"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

POST /suppressions

Create suppression.

  • Auth required: scoped project API key

Request body

FieldTypeRequiredDescription
channelenumYesemail, sms, webhook
recipientstringYesRecipient address/number/url
reasonenumNohard_bounce, spam_complaint, manual
sourceenumNosystem, client
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/suppressions \
  -H "Content-Type: application/json" \
  -H "X-API-Key: PROJECT_KEY" \
  -d '{"channel":"email","recipient":"[email protected]","reason":"manual","source":"client"}'
JSON
{
  "id": "90763e0e-e417-44a5-b0d4-ec9793fdf52f",
  "channel": "email",
  "recipient": "[email protected]",
  "reason": "manual",
  "source": "client"
}

DELETE /suppressions/{id}

Delete suppression.

  • Auth required: scoped project API key
  • Response: 204 No Content
Bash
curl -X DELETE https://beaco.michaelogunjimi.com/api/v1/suppressions/90763e0e-e417-44a5-b0d4-ec9793fdf52f \
  -H "X-API-Key: PROJECT_KEY"

Dead Letter Queue

GET /dead-letter

List dead-letter messages.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
statusstringactive, retried, discarded
channelstringChannel filter
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/dead-letter?status=active&channel=sms" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [
    {
      "id": "588d499e-0f53-4ac4-b7ec-a6ce77861d2e",
      "notification_id": "5f2b7371-c2d2-464b-ac53-89b683b80f68",
      "channel": "sms",
      "status": "active",
      "error_type": "provider_timeout"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

GET /dead-letter/{id}

Get dead-letter detail.

  • Auth required: scoped project API key
Bash
curl -X GET https://beaco.michaelogunjimi.com/api/v1/dead-letter/588d499e-0f53-4ac4-b7ec-a6ce77861d2e \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "id": "588d499e-0f53-4ac4-b7ec-a6ce77861d2e",
  "notification_id": "5f2b7371-c2d2-464b-ac53-89b683b80f68",
  "channel": "sms",
  "recipient": "+15551234567",
  "retry_count": 5,
  "status": "active",
  "error_message": "Twilio timeout after 30s"
}

POST /dead-letter/{id}/retry

Re-enqueue dead-letter message.

  • Auth required: scoped project API key
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/dead-letter/588d499e-0f53-4ac4-b7ec-a6ce77861d2e/retry \
  -H "X-API-Key: PROJECT_KEY"
JSON
{ "id": "588d499e-0f53-4ac4-b7ec-a6ce77861d2e", "status": "retried", "requeued": true }

POST /dead-letter/{id}/discard

Acknowledge/discard dead-letter message.

  • Auth required: scoped project API key
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/dead-letter/588d499e-0f53-4ac4-b7ec-a6ce77861d2e/discard \
  -H "X-API-Key: PROJECT_KEY"
JSON
{ "id": "588d499e-0f53-4ac4-b7ec-a6ce77861d2e", "status": "discarded" }

Analytics

GET /analytics

Get aggregate delivery metrics.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
date_fromdatetimeRange start
date_todatetimeRange end
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/analytics?date_from=2026-04-01T00:00:00Z&date_to=2026-04-17T23:59:59Z" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "total_events": 1823,
  "total_notifications": 2941,
  "delivery_rate": 0.984,
  "channel_breakdown": {
    "email": { "sent": 1500, "delivered": 1480, "failed": 20 },
    "sms": { "sent": 900, "delivered": 880, "failed": 20 },
    "webhook": { "sent": 541, "delivered": 534, "failed": 7 }
  }
}

GET /analytics/trends

Get time-bucketed notification counts.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
date_fromdatetimeRange start
date_todatetimeRange end
granularityenumhour or day
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/analytics/trends?date_from=2026-04-15T00:00:00Z&date_to=2026-04-17T23:59:59Z&granularity=day" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "granularity": "day",
  "series": [
    { "bucket": "2026-04-15", "notifications": 870 },
    { "bucket": "2026-04-16", "notifications": 1011 },
    { "bucket": "2026-04-17", "notifications": 1060 }
  ]
}

Audit Log

GET /audit-log

List audit entries.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
actionstringAction filter
fromdatetimeInclusive start datetime
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/audit-log?action=api_key.created&page=1" \
  -H "X-API-Key: MASTER_KEY"
JSON
{
  "items": [
    {
      "id": "9484f7f8-2c7e-4f20-9ca8-2ae3d6c7527f",
      "action": "api_key.created",
      "actor_api_key_id": "1f2721ab-9e73-4dfb-b60e-ea0ef8f2dc17",
      "metadata": { "new_key_name": "billing-service" }
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

Usage

GET /usage

Get API usage per endpoint per hour.

  • Auth required: scoped project API key

Query parameters

ParamTypeDescription
pageintegerPage number
per_pageintegerPage size
fromdatetimeStart
todatetimeEnd
endpointstringEndpoint filter
Bash
curl -X GET "https://beaco.michaelogunjimi.com/api/v1/usage?endpoint=/events&from=2026-04-17T00:00:00Z" \
  -H "X-API-Key: PROJECT_KEY"
JSON
{
  "items": [{ "hour": "2026-04-17T12:00:00Z", "endpoint": "/events", "request_count": 481 }],
  "total": 1,
  "page": 1,
  "per_page": 25,
  "total_pages": 1
}

Settings

Project API keys with settings:read can inspect the effective channel configuration and retry policies used by the delivery workers.

  • GET /settings/channels
  • GET /settings/retry-policies

API-key creation and rotation are human control-plane operations under /projects/{project_id}/api-keys, not settings endpoints.

Human Control Plane

Bearer-authenticated users manage the resources around notification integrations:

  • /auth/* — magic-link sessions, refresh, logout, and the current user
  • /oauth/github/* — GitHub sign-in and account connection
  • /organizations/* — organizations, members, invitations, usage, and audit history
  • /organizations/{organization_id}/projects — create and list projects
  • /projects/{project_id} — inspect, update, or delete a project
  • /projects/{project_id}/api-keys — create, list, update, rotate, or revoke scoped keys

These endpoints use Authorization: Bearer TOKEN. API-key secrets are returned only when created or rotated.

Platform Administration

/admin/* is a separate bearer-authenticated platform control plane. Access is granted through an AdminUser record and explicit permissions, not a master API key.

The current surface covers platform health, analytics, usage, audit history, API-key inspection, system accounts and credentials, administrator records, and system templates.

Health

GET /health

Public health endpoint.

  • Auth required: No
Bash
curl -X GET https://beaco.michaelogunjimi.com/api/v1/health \
  -H "X-API-Key: YOUR_API_KEY"
JSON
{ "status": "healthy" }

Pagination

All paginated endpoints return:

JSON
{
  "items": [],
  "total": 0,
  "page": 1,
  "per_page": 25,
  "total_pages": 0
}

Error Responses

401 Unauthorized

JSON
{ "detail": "Invalid or missing API key" }

403 Forbidden

JSON
{ "detail": "Insufficient permissions for this endpoint" }

404 Not Found

JSON
{ "detail": "Resource not found" }

422 Validation Error

JSON
{
  "detail": [
    {
      "loc": ["body", "recipients", 0, "phone"],
      "msg": "phone is required when channels includes sms",
      "type": "value_error"
    }
  ]
}