staqlydocs
REST API

Event types

Look up every event staqly records, so you can read the activity log or subscribe a webhook or an alert to the right ones.

Edit on GitHub

Every change in staqly (a post created, scheduled, published or failed, a channel connected, a schedule run) is recorded as an event. The same events feed the activity log, a post's history, webhooks and alerts. Use this page to pick the event types your automation should react to.

Read and subscribe to events

See what happened

List recent events, newest first (scope events:read in cloud mode):

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" "http://localhost:3333/v1/events?type=post.published"

Narrow the list with filters, for example one schedule's runs, or everything one API key did in the last week:

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" "http://localhost:3333/v1/events?schedule_id=sch_5k2nq8rx7bvm3dz4hcye"
curl -s -H "Authorization: Bearer $STAQLY_API_KEY" "http://localhost:3333/v1/events?actor_type=api_key&actor_id=key_r7zq2m4xk3pw6ta5ned2&from=2026-10-13&to=2026-10-20"

All filters are listed in Pagination & search. To download the whole log as NDJSON or CSV, use GET /v1/events/export with the same filters (see Data export).

Subscribe a webhook

To be told about events as they happen, create a webhook with the event patterns you want (scope webhooks:manage in cloud mode):

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" -X POST http://localhost:3333/v1/webhooks -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/hooks/staqly","events":["post.published","post.failed"]}'

Setup, signatures and retries are covered in Webhooks. Deliveries are at-least-once and can arrive out of order, and their body has no seq: order them by created_at (Delivery guarantees), or read this API when you need every event in exact seq order.

Event shape

Events are immutable. Each one has these fields:

FieldTypeDescription
idstringEvent ID, e.g. evt_looiyqe5kfkjh4xedcnd
seqintegerMonotonically increasing sequence number; the events cursor is based on it
typestringEvent type, e.g. post.published (tables below)
collection_idstring or nullThe collection the event belongs to, if any
post_idstring or nullThe post the event is about, if any
channel_idstring or nullThe channel, for channel events and per-target post events
schedule_idstring or nullThe schedule, for schedule events
actorobjectWho caused it: type, id, name and, when an agent or a REST client acted, agent (its name as listed under Agents) (see Actors)
dataobjectType-specific details (tables below)
created_atstringISO-8601 UTC timestamp
{
  "id": "evt_looiyqe5kfkjh4xedcnd",
  "seq": 1042,
  "type": "post.target_published",
  "collection_id": "col_qjuybkx6355nz4no6pqk",
  "post_id": "post_oalirbz67y4m4cqxq5dw",
  "channel_id": "ch_7h2kq9xw3nbvd5yhzm6a",
  "schedule_id": null,
  "actor": { "type": "system", "id": "publisher", "name": "Publisher" },
  "data": {
    "post": { "id": "post_oalirbz67y4m4cqxq5dw", "status": "publishing", "content": "v1.4 is out.", "targets": [ { "channel_id": "ch_7h2kq9xw3nbvd5yhzm6a", "status": "published", "external_url": "https://x.com/brand/status/1790000000000000042" } ] },
    "collection": { "id": "col_qjuybkx6355nz4no6pqk", "name": "Product updates" },
    "channel_id": "ch_7h2kq9xw3nbvd5yhzm6a",
    "url": "https://x.com/brand/status/1790000000000000042",
    "external_id": "1790000000000000042",
    "error": ""
  },
  "created_at": "2026-10-20T07:00:04Z"
}

Post events carry a snapshot: data.post (the post after the change, with its targets; before the deletion for post.deleted) and data.collection (id, name). Events about other objects carry them in data (data.channel, data.schedule, data.webhook).

Post events

TypeWhenExtra data
post.createdA post was created (by anyone, including a schedule that fetched it)—
post.updatedContent, media, targets, tags, metadata or collection changed via PATCH—
post.scheduledA publish time was set or moved (POST .../schedule, PATCH with publish_at, created as scheduled)publish_at, previous_publish_at when moved
post.unscheduledThe time was cleared: cancel, or PATCH with publish_at: null—
post.publish_requestedA target on your own automation waits for a resultchannel, target (channel_id, content, settings, thread), timeout_seconds
post.target_publishedOne target was publishedchannel_id, url, external_id
post.target_failedOne target failed: the platform refused, your automation reported a failure, or nothing was reported within the timeout (error No result reported in time)channel_id, error
post.reply_publishedOne thread part was published as a reply on a channelchannel_id, index, url, external_id
post.reply_failedA reply was refused or ran out of retries; the parts after it are skippedchannel_id, index, error
post.publishedEvery target is published—
post.failedA target failed, so the post did—
post.deletedA post was deleted (its history is kept)—
post.preview_sharedA preview link was madepreview_id, expires_at
post.feedback_addedSomeone commented on a preview (actor: their name)comment_id, author_name, body (first 280 characters), quote; channel_id when it is about one channel
post.purgedA post and its history were purgedIDs and counts only

Per-target events also set channel_id, so GET /v1/events?channel_id=... is one channel's publishing history.

Channel and schedule events

TypeWhendata
channel.connectedA channel was connected or reconnected (also through a connect link: the actor is the link)channel
channel_link.createdA connect link was madechannel_link_id, expires_at, max_uses
channel_link.revokedA connect link was revokedchannel_link_id
channel.updatedRenamed, settings changed, disabled or enabledchannel
channel.needs_reconnectA publish, a test or a token refresh failed with the stored credentialschannel, error
channel.disconnectedA channel was removedchannel
schedule.created, schedule.updated, schedule.deletedA schedule was created, changed or deletedschedule
schedule.ranA run finishedschedule, result (what it did, in words), published_post_ids
schedule.posts_requestedA run found nothing to publish and the schedule asks an agentschedule, collection
schedule.failedA run failed (the endpoint answered badly, a post was refused)schedule, error

Other events

TypeWhendata
collection.created, collection.updated, collection.deletedA collection was created, changed or deletedcollection
collection.archivedA collection was archived (an update that set archived)collection
webhook.created, webhook.updatedA webhook was created or changed (the secret is never included)webhook; secret_rotated on update
webhook.deletedA webhook was deletedwebhook_id
webhook.testSend test on a webhook (delivered to that webhook only)Test payload
alert.created, alert.updatedAn alert was created or changed (secrets masked)alert
alert.deletedAn alert was deletedalert_id, type
workspace.createdThe workspace was set upworkspace
workspace.updatedThe workspace was renamed or its time zone changedname, timezone (the fields that changed)
workspace.plan_changedThe plan changed: a Stripe subscription, or set by the operatorfrom, to, expires_at, reason (stripe:<status>, operator, operator_comp)
workspace.credits_addedA bought bundle of extra posts to X was paid for (Stripe Checkout)provider (x), posts, expires_at (the end of the billing period)
workspace.account_attachedA workspace without an account joined one: its first owner accepted an owner invitation (e.g. one the operator handed over)account_id, owner_user_id, sessions_revoked, api_keys_adopted, connect_links_expired
workspace.export_requestedA workspace export was requestedexport_id
workspace.exportedA workspace export finishedexport_id, size_bytes
retention.events_prunedThe retention worker deleted old eventscount, older_than_days, before
api_key.createdAn API key was created (cloud), including the admin key a recovery issuesapi_key (no secret)
api_key.rotatedAn API key was rotatedold_key_id, new_key_id, name
api_key.revokedAn API key was revokedapi_key_id
session.signed_inA browser signed in to the dashboard: with an admin API key (cloud; the actor is that key), a person's sign-in (cloud accounts) or the access token (local, via: "access_token")user_agent, session_id; api_key_id, user_id or via
session.recoveredSomeone signed in with the recovery key; a new admin key was created and every other session signed outuser_agent, sessions_revoked, api_key_id (the new admin key)
session.revokedA browser session signed out or was revokedsession_id, reason (logout or revoked)
access_token.rotatedStaqly Community Edition: the access token was replaced, from the dashboard or with server token --rotate (every other browser was signed out)sessions_revoked
recovery_key.rotatedThe recovery key was rotated (other sessions were signed out)sessions_revoked; via: "cli" when rotated with server recovery-key, via: "recovery" when a recovery used it up
device_link.createdA Connect a device link was createdexpires_at
device.connectedA Connect a device link was redeemeduser_agent
sign_in_method.addedA person added a passkey or an email address to how they sign in (cloud)provider; passkey_id, name or email
sign_in_method.changedA person changed their email sign-in address (cloud)provider, from, to
sign_in_method.removedA person removed a sign-in method (cloud)provider, subject

webhook.* events are recorded in the activity log but are not delivered to webhooks (except webhook.test, which goes only to the webhook being tested), so changing a webhook can't loop back to its receiver.

Audit events

The credential events above (api_key.*, session.*, recovery_key.*, access_token.*, device.*, device_link.*, sign_in_method.*) are audit events: GET /v1/events and GET /v1/events/export list them only for admins: dashboard sessions, the Community Edition's access token and keys with the admin scope. Other keys don't see them.

Webhooks and alerts get audit events only when a pattern names their family ("session.*", "api_key.revoked"), never through "*" or another glob, and only an admin may subscribe that way (403 otherwise).

Matching events in webhooks

Webhooks and alerts match events patterns against the type: exact names, "*", or globs such as "post.*" and "post.*_requested". Optionally restrict to collection_ids. See Webhooks.

Patterns are checked when you save a webhook or alert (1 to 50 of them): an exact name must be one of the types on this page (custom events such as post.shipped are rejected), and a glob must stay inside a known family (post., channel., schedule., collection., webhook., alert....). A typo such as post.publised or posts.* is rejected with 422 instead of silently never matching.

Actors

The actor field says who caused the event. Filter by it with actor_type and actor_id.

actor.typeWhoactor.idactor.name
localWhoever holds the access token in the Community Edition (agents, scripts, the CLI)localLOCAL_USER_NAME, else the computer account staqly runs as, else Local
sessionA signed-in browserThe session ID (ses_d3k2m7q4xw5tz6pa6hbn)Dashboard
api_keyAn API keyThe key ID (key_r7zq2m4xk3pw6ta5ned2)The key's name
systemstaqly itself, acting on its own or for someone outside staqlySee belowSee below

System actors:

actor.idactor.nameWhat it does
schedulerSchedulerA schedule ran, picked posts or asked for them; a scheduled post's time arrived
publisherPublisherA job published a target, or timed one out
channelsChannelsA token refresh marked a channel as needing a reconnect
retentionRetentionThe retention worker pruned data
setup, localSetup, Local setupThe workspace was created (cloud signup, first local start)
recoveryRecovery keySign-in with the recovery key (and the new admin key and recovery key it issues)
device-linkConnect a deviceA device link was redeemed
cliServer CLIserver recovery-key on the server
operatorOperatorA plan set on the server by the operator
stripeStripeA Stripe subscription change

Next steps

On this page