backend/openapi/beeos-platform-v1.yaml) and the SDKs generated
from it (@beeos-ai/sdk for TypeScript, github.com/beeos-ai/sdk-go
for Go) are documented in this file.
This file is the canonical change log that:
- end users see when they bump SDK versions, and
- the publish workflow consults when minting a tag (the SDK
generator stamps the version in
sdks/openapi-sdk/generate.sh).
backend/docs/adr/.
The format is based on
Keep a Changelog and the
contract version follows Semantic Versioning.
Versioning
| Bump | Triggered by |
|---|---|
| MAJOR | Removing routes, renaming required fields, changing the shape of an existing response, changing auth semantics (e.g. tightening the ownership model), changing error code semantics. |
| MINOR | Adding new routes, adding optional request fields, adding optional response fields, adding new code values. |
| PATCH | Documentation, examples, OpenAPI description polish, generator config tweaks that don’t change emitted code. |
Release process — how a version actually ships
- Edit the spec. All wire changes land in
backend/openapi/beeos-platform-v1.yamlfirst. The openapi-gateway service consumes this file viago:embed, so ago buildinbackend/services/openapi-gatewayis the first lint. - Sync the vendored copy. From
sdks/openapi-sdk/: - Update generator stamps. Bump
npmVersion=andpackageVersion=insdks/openapi-sdk/generate.shin the same commit (lines 88–95). Both SDKs share the version. - Write the entry below. Add a new section at the top of
the
[Unreleased]block; describe wire changes only. Implementation-side refactors that don’t affect any consumer bytes belong in the relevant service README, not here. - Open the PR. CI runs
redocly lint+make gen+ the contract-drift test inbackend/services/openapi-gateway/internal/dto/contract_test.go, plusgo build ./...on the generated Go SDK. - On merge, the publish workflow: stamps the changelog
header with today’s date, tags the meta repo
sdk-v<version>, publishes@beeos-ai/sdkto npm, and pushes the generated Go SDK togithub.com/beeos-ai/sdk-go(which is itself a release-tagged repo sogo getresolves the new tag).
sdks/beeos-ai-sdk/ or sdks/beeos-ai-sdk-go/
by hand — they are regenerated on every release. The only
hand-curated assets are under
sdks/openapi-sdk/assets/{beeos-ai-sdk,beeos-ai-sdk-go}/ and
get copied on top of the generator output (README, LICENSE).
[Unreleased]
Added (Message Envelope v3 — MessageDTO alignment)
MessageDTOnow references canonical v3 unions. Thestate,stop_reason, andpartsfields previously inlined their enums / array-of-object schema inMessageDTOonly. They now$refthe platform-wideMessageState,StopReason, andPartschemas — the same union shapes already exposed onTaskSSEMessageand the streaming envelopes. Generated SDKs gain typedMessageState/StopReason/Partimports automatically; raw-JSON callers see no wire change (the union values were already the same string set on the wire — this is a contract-level dedupe).MessageDTO.updated_at(RFC3339Nano, UTC). Server’s last-PATCH timestamp on v3 envelopes. Emitted only when distinct fromcreated_at— fresh POST replies omit it, streaming envelopes mutate it on every PATCH, terminal rows freeze it at the moment of state transition. SSE replay clients SHOULD use it to ack progress checkpoints; chat-log UIs MAY ignore it. Empty / absent on legacy v1 rows.
Fixed (SDK guide typos)
- TypeScript guide:
reply.data?.replycorrected toreply.data?.text;task.data?.status === "completed"→"succeeded"(the actualTaskStatusterminal value);conv.data!.id→conv.data!.conversationId(matches the generatedConversationResponseAllOfDatafield); dropped the non-existentsize_bytesfield onPresignUploadRequest. - Go guide:
reply.GetData().GetReply()→GetText();status == "completed"→"succeeded"; droppedSizeBytesfromPresignUploadRequest. - Quickstart guide: same
reply→textrename in both TypeScript and Go examples.
Breaking (scope vocabulary removed — v1.1.0)
oag_User API Key per-route scopes deleted. The setagents:read,agents:write,tasks:read,tasks:write,files:read,files:write,instances:read,instances:write(and theadmin:*wildcard) is no longer recognised. The 37 routes that previously required a scope are now governed purely by owner-ACL — everyoag_key has full access to its owner’s resources, the same way a JWT does.403 insufficient_scopeis no longer emitted. Drop any code that branches onerror.code === "insufficient_scope"; fold the case into your genericforbidden(403) handler.POST /api/v1/api-keysignoresscopesin the request body and the response no longer carriesscopes. SDK callers should remove the argument fromcreateAPIKey(...)— it is silently dropped on the wire during the rolling deploy window and will be removed from the request schema entirely in the next contract revision.- No data migration required. Existing
oag_keys keep working with full owner-level access; theapi_keys.scopescolumn is dropped server-side.
Breaking (ADR-0022 — dual-layer message storage)
GET /api/v1/agents/{agentId}/conversations/{convId}/messagesandGET /api/v1/agents/{agentId}/tasks/{taskId}/messagesnow default-filter out ephemeral streaming chunks (agent_reply_delta,agent_thought_chunk,agent_message_chunk). Add?include_deltas=trueto opt back in.latest_offsetcontinues to reflect the TRUE server-side max offset across both filtered and unfiltered rows, sosince=/ cursor-style pagination is unaffected. SSE/eventsis NOT affected — live consumers continue to receive every envelope as it happens. Migration recipe: client code that rebuilt streaming transcripts by polling/messagesshould either (a) passinclude_deltas=trueto preserve current behaviour, or (b) switch to the SSE/eventsstream which is the supported way to consume live token chunks.- SSE
/eventsmay emit a newbackfill_truncatedevent frame when the client reconnects withLast-Event-IDpointing before the oldest retained ephemeral chunk. Older SDKs that don’t recognise the frame should treat it as a hint to resume fromlatest_offset(returned inreplay_complete) instead of replaying individual chunks. Per ADR-0022 §6 the retention window is 24h / 10k entries by default. - SSE event
offsetclarified as monotonic-but-not-contiguous. Producer-side write failures may leave small holes in the offset sequence (e.g.… 40, 42, …with41missing). Clients MUST treatoffset > sinceas the resume invariant — code that assertsoffset == since + 1was always undefined behaviour and is now explicitly documented as a bug. Seeguides/streaming.mdfor the resume recipe.
Added
POST /api/v1/agents/{agentId}/tasks/{taskId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver— manual re-queue of a single webhook delivery attempt. Returns a newpendingWebhookDeliveryResponse. Useful when the receiver was down and you want to replay without waiting for the next exponential-backoff slot.GET /api/v1/agents/{agentId}/tasks/{taskId}/webhooks/{webhookId}/deliveries— per-attempt delivery audit log for a webhook binding. Returns the last N attempts (default 50, max 100) includingstatus,attempt_num,last_response_status,last_error,next_attempt_at.secret(optional, write-only) field onRegisterTaskWebhookRequestand the underlying A2ApushNotificationConfig/set. When supplied, every delivery carriesX-BeeOS-Signature: sha256=<hex>computed over the JSON body with HMAC-SHA256 keyed bysecret. The wire response only echoeshas_secret: true; the raw value is never returned. Seedocs/guides/webhooks.mdfor the verification recipes in Python and Node.js.- Webhook retry & dead-letter. Failed deliveries (network
error, timeout, or 5xx response) are retried with exponential
backoff at
1m / 5m / 30m / 2h / 12h. After the 6th failed attempt the row becomesdead_letter. 4xx responses (other than 408 / 429) are NOT retried — they’re treated as a permanent client misconfiguration. Driven by a background worker inbackend/services/a2apolling a durablewebhook_deliveriestable. GET /api/v1/tasks— cross-agent task list. Returns all tasks the authenticated caller created via the OpenAPI surface, optionally filtered byagent_id. Pages withlimit+since. Useful for multi-agent UIs (one inbox across an organization’s agents).POST /api/v1/files/presign-upload— request a short-lived S3-style PUT URL for uploading a file. Returns afile_idthat can be embedded in subsequentinvoke/tasks.create/conversations.sendbodies asattachments[].file_id.GET /api/v1/files/{fileId}— read-back of a previously uploaded file’s metadata + presigned GET URL.attachments[].file_idonInvokeAgentRequest,CreateTaskRequest, and conversation send bodies. The agent sees a normal multi-partchat_messagewithfileparts.PATCH /api/v1/agents/{agentId}— owner-only update of an agent’svisibilityandmcp_enabledflags. Other agent fields (name,description, …) are still owner-synced via the agent process’sPOST /api/v1/agents/syncand cannot be patched here (changes would be silently overwritten on next sync).metricsendpoint (GET /metrics) — Prometheus pull endpoint exposing standard process metrics plusbeeos.openapi.*namespaces. OTLP push remains available viaOTEL_EXPORTER_OTLP_*env vars.- OpenAPI-level rate limiting on probe routes.
/healthz,/version,/spec/*,/metricsare now per-IP rate-limited to prevent unauthenticated scraping DoS. MaxBytesReaderbody-size enforcement. Default 1 MiB per request, 64 KiB on catalog/list endpoints. Over-limit returns413 payload_too_large.timeout_msclamp onPOST /agents/{id}/invoke. The caller-supplied value is clamped to[100, 115_000](the upstream end-to-end budget is 120s). Going past the clamp surfacesservice_timeoutinstead of a stale connection.- Examples in OpenAPI for the top 5 operations
(
listInstances,listAgents,invokeAgent,createTask,getTask) plus all 4xx/5xx response shapes.
Changed
- All error responses now follow the canonical envelope
with
codedrawn from the table indocs/reference/errors.md. 4xx and 5xx responses share the same shape; older clients that only inspected the HTTP status keep working. - Webhook
Current limitationssection indocs/guides/webhooks.mdhas been rewritten — what used to be “limitations” (no signing, no retry, no audit log) are now first-class features. The only remaining caveat is the 6-attempt cap before dead-letter.
Documentation (no wire change)
- New
docs/architecture/public-overview.md— the four public hosts, what each owns, and cross-protocol invariants. - New
docs/guides/mcp-gateway.md— MCP integration walkthrough (OAuth / DCR / PKCE + API-key paths). - New
docs/guides/a2a-external.md— federating an external agent platform viaa2a.beeos.ai. - New
docs/guides/conversations.md— the multi-turn dialog surface. - New
docs/guides/agent-author-quickstart.md— building the agent process itself (the other side of every guide). - New
docs/guides/streaming.md— the three SSE surfaces,since=<offset>, reconnect. - New
docs/guides/choosing-a-protocol.md— OpenAPI vs A2A vs MCP, side-by-side. - New
docs/reference/errors.md— canonicalcodetable. - New
docs/guides/auth-api-keys.md— JWT vsoag_vsbak_vs OAuth lifecycle. - Rewrote
docs/guides/calling-agents.mdto cover all three invocation modes (blocking / SSE / async task) with TS + Go SDK snippets. - Added
docs/guides/sdk-migration.md— per-version migration recipe (start of the migration log).
[0.4.0] — 2026-04-30
Breaking
SendMessageResponse.offsetremoved. The field was a misleading placeholder; the real offset is kept opaque inside the cursor. Callers that need the offset for SSE resume must round-trip viaGET /messages?since=<cursor>(unchanged for SSE consumers).
Added
InvokeAgentRequest.idempotency_keyandInvokeAgentRequest.metadata(both optional; backwards-compatible). The sameidempotency_keyis honoured end-to-end through L0 → MS.TaskResponse.truncated(optional boolean) — signals that the task’s event log was scanned past the 1 000-message cap and the returned events may not be the full set. Old clients may ignore this field.PushNotificationConfig.protocol_filter— gateways stamp the originating renderer key (openapi/a2a/mcp) when registering. Lets the same backing webhook be filtered by triggering surface.
[0.3.0] — 2026-03-22
Added
- First public-grade contract:
POST /api/v1/agents/{id}/invoke(sync + SSE).POST /api/v1/agents/{id}/tasks+GET .../tasks/{id}+GET .../events(SSE) +POST .../cancel.POST /api/v1/agents/{id}/conversations+ send + delete + list withsincecursor.- Task webhooks:
POST .../webhooks+GET+DELETE. - Files presign (uploads).
idempotency_keyoninvokebody.
@beeos-ai/sdk@0.3.0 and github.com/beeos-ai/sdk-go@v0.3.0
are still resolvable from npm / proxy.golang.org and remain
wire-compatible with the current openapi-gateway for the
overlapping route set.
Earlier versions
0.2.x and earlier predate the OpenAPI Gateway BFF decoupling
(ADR-001) — they fronted the main Gateway directly and are
no longer part of the supported SDK contract. Consumers stuck
on those versions should bump straight to 0.4.x and follow
docs/guides/sdk-migration.md.