API Reference

Beacon provides a REST API for direct integration when an SDK is not suitable for your environment. The API is used internally by all Beacon SDKs.

Authentication

All ingestion API requests require an API key passed as a Bearer token in the Authorization header:

curl -X POST https://api.beacon.softagility.com/v1/events \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '[{
    "event_id": "01912345-6789-7abc-def0-123456789abc",
    "category": "reports",
    "name": "report_exported",
    "timestamp": "2026-03-20T14:30:00Z",
    "actor_id": "user-123",
    "source_app": "MyApp",
    "source_version": "1.2.0",
    "properties": { "format": "pdf", "row_count": "1500" }
  }]'

Base URL

https://api.beacon.softagility.com/v1

Ingestion Endpoints

MethodEndpointDescription
POST/v1/eventsTrack one or more events (batch, up to 1000)
POST/v1/events/sessionsStart a new session
POST/v1/events/sessions/endEnd an active session
POST/v1/events/exceptionsReport an exception with stack trace

POST /v1/events

Send a JSON array of event objects. Per-request limits: max 1 MB total payload, max 1000 events per batch. Per-event limit: each event object must be ≤ 4 KB serialized JSON; oversize individual events are rejected at the per-event level (the batch as a whole still completes).

Required fields per event:

  • event_id — UUID (v7 recommended)
  • category — String, max 128 chars
  • name — String, max 256 chars
  • timestamp — ISO 8601 datetime
  • actor_id — String, max 512 chars
  • source_app — Your product’s slug (the value created on the Products page in the Beacon dashboard). Must match a registered product or the event is rejected with UNRECOGNIZED_PRODUCT. Max 256 chars.
  • source_version — Your application’s version string. Max 128 chars. By default, unknown values are auto-registered on first sight. If strict version mode is enabled for the product, values that aren’t pre-registered (or whose registered version is archived) are rejected with UNREGISTERED_VERSION and the rejected events are permanently dropped — the SDK does not retry.

Optional fields:

  • properties — Flat key/value object (no nested objects or arrays). Max 20 keys; key max 64 chars; value max 256 chars.
  • session_id — UUID linking event to a session.
  • schema_version — Optional caller-provided schema version. Recorded but not validated.

Response codes:

  • 200 — every event in the batch was accepted.
  • 207 — at least one event-level rejection occurred (including the all-rejected case). The response body lists per-event acceptance/rejection results so the caller can identify which failed.
  • 400 — request itself was malformed (not a JSON array, empty array, or batch exceeding the 1000-event-count limit).
  • 401 — invalid or missing API key.
  • 402 — ingestion is blocked for the tenant’s billing state. See 402 behavior below.
  • 413 — request body exceeded the 1 MB transport limit.
  • 429 — rate limit exceeded. Includes a Retry-After header indicating how long to wait.

POST /v1/events/sessions

Start a new session.

Required fields: session_id, actor_id, source_app, source_version, started_at

Optional fields: properties — flat key/value object, same property limits as events (max 20 keys; key max 64 chars; value max 256 chars).

Response: 200 with { "session_id": "...", "status": "accepted" }.

POST /v1/events/sessions/end

End an active session.

Required fields: session_id, ended_at.

Optional fields: end_reason — defaults to "normal" if omitted. Valid client values: "normal" (graceful shutdown) and "sdk_recovery" (the SDK detected a previously-orphaned session and is closing it). The value "timeout" is reserved for server-side timeout marking and is rejected if sent by a client.

POST /v1/events/exceptions

Report an exception.

Required fields: exception_id, exception_type, severity (fatal or non_fatal), occurred_at, actor_id, source_app, source_version.

Optional fields:

  • message — recorded if ≤ 1000 chars; truncated to 1000 chars if longer (not rejected).
  • stack_trace — recorded if ≤ 32,768 chars; truncated to 32,768 chars if longer.
  • breadcrumbs — any non-null JSON value (typically an array of crumb objects). Beacon does not enforce a specific shape here; see the SDK guides for the convention each SDK uses.
  • session_id — links the exception to a session if both exist. An invalid or unknown UUID is silently ignored rather than rejected.
  • environment_context — application/runtime context (OS, version, etc.) typically populated automatically by the SDK.

Response: 200 with { "exception_id": "...", "status": "accepted", "fingerprint": "..." }.

Rate Limits

Rate limiting applies to POST /v1/events only. Sessions and exceptions endpoints are not currently rate-limited.

Limits are per-minute and configured per plan:

PlanRequests per minute
Trial30
Starter120
Pro600
Business3000
Enterprise6000

When the limit is exceeded, the API returns 429 with a Retry-After header indicating how many seconds to wait before retrying. (No X-RateLimit-* headers are emitted today; consult Retry-After only.)

402 behavior

402 Payment Required is returned when the tenant’s billing state blocks ingestion. Three triggering conditions:

  • Hard cap reached — the tenant has exhausted its monthly event allotment AND its plan policy is to hard-cap on overage (Trial and Starter, where overage is not billable). On charge-policy plans (Pro, Business, Enterprise), exceeding the included allotment generates overage billing instead of a hard cap.
  • Trial expired — the 21-day trial has ended without conversion to a paid plan.
  • Subscription cancelled — the subscription was cancelled and the grace period has elapsed.

Cap-policy plans hard-cap only after their grace period. Recovery paths: upgrade to a paid plan, reactivate a cancelled subscription, or wait for the next billing cycle (cap-policy plans only). SDKs queue events to local persistent storage when they receive 402 and retry automatically once the state clears.

Version registrations

POST /v1/version-registrations registers a (product, version) pair so that ingestion can accept events for that version when strict version mode is enabled. Requires the manage_products permission. When strict mode is off (the default), this endpoint is optional — versions are auto-registered on first ingestion.

Required fields:

  • source_app — Product slug.
  • source_version — Version string. Max 128 chars.

Optional fields:

  • release_date — ISO 8601 date (YYYY-MM-DD) the version was released to users. Captured today; consumed by the version-selector “Latest” tie-breaker and reserved for future release-window analytics. May be set to null (or omitted).

Success response: 201 Created

{
  "id": "01912345-...",
  "source_app": "my-product",
  "source_version": "1.2.0",
  "release_date": "2026-05-01",
  "created_at": "2026-05-10T14:30:00Z"
}

Error responses:

  • 400 validation_error — missing or malformed required field.
  • 403 forbidden — caller lacks manage_products.
  • 404 product_not_foundsource_app doesn’t match any product in the tenant.
  • 409 product_deleting — the target product is in the deleting lifecycle state and won’t accept new versions.
  • 409 version_already_registered — the (product, version) pair already exists. Use the version update endpoint below to change release_date or status rather than re-posting.

PATCH /v1/products/{productId}/versions/{versionId}

Updates an existing version. Used for editing release_date and for archiving/unarchiving (the canonical “delete” semantic — there is no hard-delete endpoint).

Optional PATCH fields:

  • release_date — set or change the release date. Passing null (or omitting the field) leaves the existing value unchanged — there is no explicit clear semantic today.
  • status"active" or "archived". Archived versions are rejected by strict-mode ingestion (UNREGISTERED_VERSION); flipping back to active resumes ingestion.

Requires manage_products.

For the user-facing flow, see Register a version and Strict version mode.

Product configuration on responses

Product objects returned by GET /v1/products and GET /v1/products/{id} include a strict_versions_enabled: boolean field reflecting the current state of the strict-mode toggle. Set via PATCH /v1/products/{id} with optional strict_versions_enabled?: boolean (PATCH semantics — only applied when supplied). Requires manage_products permission.

SDKs

For most integrations, we recommend using an SDK instead of the API directly. SDKs handle batching, offline persistence, session management, breadcrumbs, and retry logic automatically.