Projects

Emit an app-lane business event into the project feed

The write twin of the feed read above (app-events-emit-lane): deployed app code records ITS OWN business facts — "signature completed", "booking created" — as ordinary feed rows with source: "app", class: "app", read back by every existing and future feed consumer with zero extra work. service_key ONLY, own-project (a cross-project key gets 403, never a 404 existence oracle); principals, grant keys, and admin keys are rejected in v1 — nothing platform-side should fabricate app facts. NOT lifecycle-gated (data-plane classification): a frozen/dormant organization's app keeps serving, so its facts keep recording — gating the emit would punch silent holes in the feed exactly when the owner is debugging. event_type must be flat snake_case matching ^[a-z][a-z0-9_]{2,63}$ and must not name platform-registered vocabulary (400 RESERVED_EVENT_TYPE — e.g. mailbox_suspended is off-limits), so an app can never impersonate a platform event; every consumer discriminates app rows from platform rows structurally on (source, event_type), not by naming convention. The response next_actions[] is ALWAYS the platform-synthesized poll entry, never app-supplied — an app payload that happens to contain a next_actions-shaped field is rendered as inert data, never as an instruction (agent-instruction-injection containment). Bounded by a per-tier eventsPerDay quota pooled across the organization's projects for the UTC day (prototype 1,000 / hobby 10,000 / team 100,000): once spent, 403 QUOTA_EXCEEDED with details: {resource: "events_per_day", scope, used, limit}. idempotency_key is durable resource identity (backs the (source, source_event_id) UNIQUE, namespaced per-project server-side), not transient header-based retry dedup — the same key always replays the ORIGINAL stored event with deduplicated: true instead of writing twice.

post/projects/v1/{project_id}/events

Request

  • Base URL: https://api.run402.com
  • URL: https://api.run402.com/projects/v1/{project_id}/events
  • Auth: HTTP bearer

Path parameters

project_idstring required

Run402 project ID. Public IDs are not UUIDs.

Example:prj_1741340000_0042

Target project id. Only the project's OWN service_key may emit into its feed.

Request body

event_typestring required

Flat snake_case, 3-64 chars. 400 INVALID_EVENT_TYPE if it doesn't match the grammar; 400 RESERVED_EVENT_TYPE if it names platform-registered vocabulary.

payloadobject

Compact fact: ids + verdict fields, never bodies or secret material. Bounded to 8 KiB; an oversize payload is truncated (payload_truncated: true, dropped_keys[]) rather than rejected.

severity'info' | 'notice' | 'warn' | 'critical'

event-attention-rules: the app's own judgement of how significant this is. info routine, notice unusual but not known to be wrong, warn probably wrong, critical wrong now. Omitted stores info; the platform never infers or raises it. Routing rules filter on it (min_severity). Any other value is 400 VALIDATION_FAILED before the quota is consulted.

idempotency_keystring

Durable resource identity within this project (namespaced server-side as <project_id>:<idempotency_key> so two projects can both use e.g. order_1 without colliding on the global UNIQUE). Omit to always write a fresh row; replaying the same key returns the ORIGINAL stored event with deduplicated: true.

Example request

{
  "event_type": "signature_completed"
}

Response

Idempotency-key replay — deduplicated: true and the ORIGINAL stored event (Faithful: no pretend re-insert).

cursorstring required

This event's own opaque cursor (evc_...). Pass it as ?cursor= to resume the feed strictly after it.

event_typestring required

Echoes the request's event_type.

class'app' required

Always app for app-lane events — never a member of the platform's mandatory-delivery classes.

severity'info' | 'notice' | 'warn' | 'critical' required

The STORED severity: the request's severity, or info when it named none. On an idempotency_key replay, the original row's.

source'app' required

Always app. Consumers discriminate app facts from platform facts on (source, event_type).

payloadobject required

The BOUNDED stored payload (8 KiB contract) — echoes the request's payload, truncated when oversize.

payload_truncatedboolean

Present and true only when the stored payload was bounded to the 8 KiB cap; omitted otherwise.

occurred_atstring date-time required

Server-stamped. A client-supplied timestamp is never accepted.

deduplicatedboolean required

True when idempotency_key already existed for this project — payload/occurred_at are the ORIGINAL stored row's, not a fresh write.

Changes

No recorded changes to this operation across all 1 revision of this API.