Logs
Push feature events into Connect's audit log, filterable to this application.
Push your own feature events into Connect's audit log, so an operator debugging a customer sees your application's side of the story next to Connect's.
await connect.logs.write({
workspaceId,
action: "campaign.sent",
actorUserId,
metadata: { campaignId, recipients: 1_240 },
});Entries land in Console → Security → Audit, filtered by your application.
This is an audit trail, not a metrics pipe. One entry per meaningful, attributable action a human or your system took. Per-request logging belongs in your own observability stack - and metadata is read by operators during incidents, so keep credentials, tokens and message bodies out of it.
For counts and charts you want Analytics instead.
Endpoints
POST /api/v1/application/logs
Push a feature event into Connect's audit log
Scope logs:write · Rate limit 120 requests / min per key
Connect has no visibility into your application's own product/feature behavior - it only ever sees the interactions in this reference (sign-ins, RBAC/subscription checks). This endpoint lets you push your own structured events here instead, so they show up in the Console's audit log - filtered to your application from its own page - without needing a separate observability stack.
Implementation
1. Prerequisite - an API key scoped to logs:write.
2. Send events as they happen:
curl -X POST https://connect.cosmoops.com/api/v1/application/logs \
-H "Authorization: Bearer YOUR_API_KEY_PLAINTEXT" \
-H "Content-Type: application/json" \
-d '{"level":"info","message":"campaign sent","metadata":{"campaignId":"..."}}'level defaults to "info" if omitted. metadata is capped at ~16KB serialized. timestamp defaults to receipt time - only honored if it parses, and never trusted to reorder ingestion.
3. Attribute it to a user by passing that user's sessionToken - the same opaque token /sso/token handed you at sign-in, and the one /sso/userinfo takes:
-d '{"level":"info","message":"campaign sent","sessionToken":"THEIR_SESSION_TOKEN"}'Both scopes on the stored entry come from something verified, never from your body: applicationId from the API key, and userId from this token - which is checked to be unrevoked, unexpired, and minted for your application before anything is attributed. There is no userId field to send; a caller-typed actor would make the log stream unusable as a record of who did what.
The response's attributed says whether the token resolved. A token that has expired (~15 days) or was revoked yields attributed: false and the event is still stored, just without a user - losing telemetry because an attribution failed would be the worse outcome of the two.
4. That's it - entirely optional, fire-and-forget, and only ever visible in this application's own Logs section, never mixed with another application's or with Connect's own audit trail.
Errors
validation_error (400), forbidden (403 - key missing logs:write), rate_limited (429 - meaningfully higher than the read endpoints since this is meant to carry real event volume, not an occasional check).
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
level | debug | info | warn | error | no | |
message | string | yes | |
metadata | object | no | |
timestamp | any | no | |
sessionToken | string | no |
Responses
| Status | Meaning |
|---|---|
201 | Logged |
400 | validation_error |
403 | forbidden - key missing logs:write |
429 | rate_limited |
Response body
| Field | Type | Required | Notes |
|---|---|---|---|
success | boolean | yes | |
attributed | boolean | yes |