messages — edge message store API
cyoda-go version 0.8.2
messages
Section titled “messages”messages — edge message create, read, and delete REST API.
SYNOPSIS
Section titled “SYNOPSIS”POST /api/message/new/{subject}GET /api/message/{messageId}DELETE /api/message/{messageId}DELETE /api/messageContext path prefix is CYODA_CONTEXT_PATH (default /api). All endpoints require Authorization: Bearer <token> except when CYODA_IAM_MODE=mock. No role is required beyond a valid token.
DESCRIPTION
Section titled “DESCRIPTION”An edge message is an arbitrary JSON payload stored under a server-generated time-UUID together with a fixed set of AMQP-aligned headers and an optional flat metadata map. The store is standalone: a message is not an entity, and creating one does not touch the workflow engine — no transition fires, no processor or criterion runs. Edge messaging is a durable, tenant-scoped staging buffer at the platform edge, a peer of the entity store rather than part of it.
The surface is HTTP-only; there is no gRPC message service.
Messages are keyed by (tenant, messageId). The tenant is derived from the authenticated caller, never from a request parameter, so a message ID only resolves within the tenant that created it (see TENANT ISOLATION).
Body size limit on all write endpoints: 10 MiB.
MESSAGE SHAPE
Section titled “MESSAGE SHAPE”The create request and the read response are different shapes: you POST a payload plus metadata, and you GET back the same payload and metadata wrapped alongside the stored header. The metadata values round-trip unchanged — a flat map in, the same flat map out.
Create request — a single JSON object:
payload(required): any valid JSON value — object, array, string, number, boolean, ornull. It is stored verbatim (whitespace is compacted).Content-Typeis informational only; the store does not parse the payload against it. Non-JSON content must be stringified by the client (for example base64) and sent as a JSON string.metaData(optional): a flat map of string keys to JSON values, stored alongside the message and returned verbatim on read. There is no message-query endpoint — messages are retrieved by ID, not searched by metadata.
Read response (EdgeMessageDto) — three top-level fields:
header: the storedEdgeMessageHeader(below); empty optional fields are omitted.metaData: the metadata map, returned flat and unchanged — the samemetaDatasupplied at creation.content: the payload, echoed back verbatim as it was stored.
Header (EdgeMessageHeader) — AMQP-aligned:
subject(string): taken from the{subject}path segment on create.contentType(string): from theContent-Typerequest header; informational.contentLength(int64): from theContent-Lengthrequest header.contentEncoding(string): from theContent-Encodingrequest header (defaultUTF-8).messageId,userId,recipient,replyTo,correlationId(all optional strings): from theX-Message-ID,X-User-ID,X-Recipient,X-Reply-To,X-Correlation-IDrequest headers respectively. These are caller-supplied envelope fields and are distinct from the server-generated store ID in the URL.
ENDPOINTS
Section titled “ENDPOINTS”POST /api/message/new/{subject} — Create and store a message
subject(path): string matching^[a-zA-Z0-9._-]{1,256}$Content-Type(header, required): MIME type of the payload (informational)Content-Length(header, required): payload size in bytesContent-Encoding(header, optional): defaultUTF-8X-Message-ID,X-User-ID,X-Recipient,X-Reply-To,X-Correlation-ID(headers, optional): AMQP envelope fields, each 1..1024 charstransactionTimeoutMillis(query, optional): int64, default10000— accepted for Cyoda Cloud parity; parsed but currently has no behavioural effect in cyoda-go.
Request body: a NewMessageRequest object { payload, metaData } as described in MESSAGE SHAPE. Missing payload returns 400 BAD_REQUEST.
The server generates a time-UUID for the message and a separate time-UUID for the transaction. Response: 200 OK, application/json, an EntityTransactionResponse array with a single element:
[{ "entityIds": ["8824c480-c166-11ee-bf9f-ae468cd3ed16"], "transactionId": "9f3a0000-c166-11ee-bf9f-ae468cd3ed16"}]GET /api/message/{messageId} — Retrieve a message by ID
messageId(path): UUID
Response: 200 OK, application/json, an EdgeMessageDto:
{ "header": { "subject": "nobel.prize.events", "contentType": "application/json", "contentLength": 1024, "contentEncoding": "UTF-8", "messageId": "msg-nobel-2024-physics", "userId": "nobel-committee", "recipient": "scientific-community", "replyTo": "announcements@nobelprize.org", "correlationId": "nobel-2024-physics-announcement" }, "metaData": { "eventType": "nobel.prize.announced", "timestamp": "2024-10-09T12:00:00Z" }, "content": { "category": "physics", "year": "2024" }}A malformed messageId returns 400 BAD_REQUEST; a well-formed UUID that does not exist for the calling tenant returns 404 ENTITY_NOT_FOUND.
DELETE /api/message/{messageId} — Delete a single message
messageId(path): UUID
Deletes the message and its payload blob. Returns 404 ENTITY_NOT_FOUND if the ID does not exist for the tenant. Response: 200 OK, application/json, a MessageDeleteResponse:
{ "entityIds": ["8824c480-c166-11ee-bf9f-ae468cd3ed16"] }Message delete is not transactional in the same sense as entity create/update.
DELETE /api/message — Delete multiple messages by ID
transactionSize(query, optional): int32, default1000— accepted for Cyoda Cloud parity; parsed but currently has no behavioural effect in cyoda-go.
Request body: a JSON array of UUID strings. Every element must parse as a UUID; otherwise 400 BAD_REQUEST. Response: 200 OK, application/json, a MessageDeleteBatchResponse array:
[{ "entityIds": [ "8824c480-c166-11ee-9cc7-ae468cd3ed16", "31134900-d9cb-11ee-9cc7-ae468cd3ed16" ], "success": true}]TENANT ISOLATION
Section titled “TENANT ISOLATION”Every operation is scoped to the authenticated caller’s tenant. The tenant is resolved from the user context, and each backend keys and filters messages by (tenant, messageId):
- postgres / sqlite — the primary key is
(tenant_id, message_id)and every query filters ontenant_id. - memory — metadata is held per tenant and each payload blob lives under a per-tenant directory.
A message ID is an unguessable time-UUID and is only ever retrievable or deletable by the tenant that created it. A cross-tenant read or delete resolves to 404 ENTITY_NOT_FOUND, not a leak.
ERRORS
Section titled “ERRORS”Errors are RFC 9457 application/problem+json with properties.errorCode set to the machine-readable code:
errors.BAD_REQUEST—400— invalid JSON, missingpayload, malformedmessageId, or a delete body that is not a JSON array of UUID stringserrors.ENTITY_NOT_FOUND—404— no message with this ID exists for the calling tenant (get and single-delete)413— request body exceeds the 10 MiB limit (create and batch-delete)401— missing or invalid Bearer token403— authenticated but not authorized500— internal error; generic message plus a ticket UUID, full detail logged server-side
EXAMPLES
Section titled “EXAMPLES”Create a message:
curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "X-Correlation-ID: nobel-2024-physics" \ -d '{ "metaData": { "eventType": "nobel.prize.announced" }, "payload": { "category": "physics", "year": "2024" } }' \ "http://localhost:8080/api/message/new/nobel.prize.events"Retrieve a message:
curl -s -H "Authorization: Bearer $TOKEN" \ "http://localhost:8080/api/message/8824c480-c166-11ee-bf9f-ae468cd3ed16"Delete a single message:
curl -s -X DELETE \ -H "Authorization: Bearer $TOKEN" \ "http://localhost:8080/api/message/8824c480-c166-11ee-bf9f-ae468cd3ed16"Delete several messages by ID:
curl -s -X DELETE \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '["8824c480-c166-11ee-9cc7-ae468cd3ed16","31134900-d9cb-11ee-9cc7-ae468cd3ed16"]' \ "http://localhost:8080/api/message"SEE ALSO
Section titled “SEE ALSO”- crud
- cloudevents
- openapi
- errors.ENTITY_NOT_FOUND
- errors.BAD_REQUEST
See also
Section titled “See also”cyoda help crud— Entities are instances of models. Each entity has a UUID, a model reference (entityName,modelVersion), and a lifecycle state managed by the workflow engine. Creating an entity requires the referenced model to be inLOCKEDstate. All write operations run within a Cyoda transaction and return atransactionIdalongside the affected entity IDs.cyoda help cloudevents— cyoda-go uses CloudEvents v1.0 as the transport envelope for event-driven processing. Every payload carried inside a CloudEvent — workflow calculation requests, calculation responses, entity transaction events, snapshot state, model descriptors — is validated against a JSON Schema (Draft 2020-12).cyoda help openapi— cyoda-go generates its OpenAPI 3.1 specification from the embeddedapi/openapi.yamlfile compiled into the binary at build time. The spec is served at/openapi.jsonwith runtime-patched server URLs. The Scalar API Reference UI is served at/docsand loads the spec from/openapi.json.cyoda help errors ENTITY_NOT_FOUND— No entity with the given ID exists in the tenant’s data store, or the entity existed at a point-in-time that precedes the requested snapshot. Also returned for audit log lookups when the specified event or message cannot be found.cyoda help errors BAD_REQUEST— Fired when the server cannot parse or structurally process the incoming request. Common triggers include invalid JSON, missing required fields, unsupported format specifiers, or mutually exclusive parameters being set simultaneously.
Raw formats
Section titled “Raw formats”/help/messages.json— full descriptor (matchesGET /help/{topic}envelope)/help/messages.md— body only