Platform API
Every Platform action (19) and data model (1): endpoints, scopes, input, preconditions, events and examples.
API keys, webhooks and background jobs for connecting other tools.
Module code platform · 19 actions · 1 data models · 16 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: platform (everything), platform:read (read only) or platform:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
event_subscriber_skips | event_subscriber_skip | /api/v1/event_subscriber_skips |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
platform.cancel_job | platform:manage | Cancel a job |
platform.create_api_key | platform:manage | Create an API key |
platform.create_webhook | platform:manage | Register a webhook |
platform.delete_webhook | platform:delete | Remove a webhook |
platform.end_session_key | platform:read | End an Era session key |
platform.enqueue_job | platform:handle | Queue work |
platform.issue_session_key | platform:read | Start an Era session key |
platform.issue_station_key | platform:read | Start Era’s engine for me |
platform.pair_device | platform:read | Pair my computer |
platform.purge_history | platform:delete | Sweep delivery history |
platform.record_delivery | platform:handle | Record a webhook delivery |
platform.rename_api_key | platform:manage | Rename an API key |
platform.retry_job | platform:manage | Retry a job |
platform.revoke_api_key | platform:manage | Revoke an API key |
platform.rotate_webhook_secret | platform:manage | Rotate a signing secret |
platform.send_test_delivery | platform:manage | Send a test or replay a delivery |
platform.set_webhook_active | platform:manage | Switch a webhook on or off |
platform.unpair_device | platform:read | Remove my computer |
platform.update_webhook | platform:manage | Edit a webhook |
platform.cancel_job
Cancel a job — Takes a queued job off the queue. A job already running is left alone.
Needs platform:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/actions/platform.cancel_jobInput
| Field | Type | Required | Notes |
|---|---|---|---|
job_id | uuid | yes | Job |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
job_not_found | No such job in this workspace. |
job_already_finished | That job has already finished. |
job_running | That job is running and cannot be cancelled from here. |
Events it publishes: platform.job_cancelled
What it writes: job (update) — Marks the job cancelled
curl -X POST https://app.getera.io/api/v1/actions/platform.cancel_job \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"job_id":"JOB_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.job_cancelled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.cancel_job", {
job_id: "JOB_ID"
})era platform cancel_job \
--job-id "JOB_ID"{
"name": "era__platform__cancel_job",
"arguments": {
"job_id": "JOB_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 job_not_found, job_already_finished, job_running · 429 slow down
platform.create_api_key
Create an API key — Issues a key for this workspace. The key itself is shown once and never stored.
Needs platform:manage · changes data
403).https://app.getera.io/api/v1/actions/platform.create_api_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Name · What this key is for. It is the only thing that will identify it later. · max 120 |
key_hash | string | yes | Key digest · SHA-256 of the key. The key itself never reaches the server twice. · max 64 |
key_prefix | string | yes | Key prefix · The first few characters, so the key can be told apart on screen. · max 16 |
scopes | string | Modules · Comma-separated module codes. Leave empty to inherit whatever the creator holds. · max 500 | |
expires_at | date-time | Expires |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_a_digest | The key digest must be a SHA-256 hash, not the key itself. |
name_taken | This workspace already has a key with that name. |
expiry_in_past | The expiry date has already passed. |
key_limit_reached | This workspace already has the maximum number of live keys. |
Events it publishes: platform.key_created
What it writes: api_key (insert) — Stores the digest, the prefix and who issued it
Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_a_digest, name_taken, expiry_in_past, key_limit_reached · 429 slow down
platform.create_webhook
Register a webhook — An endpoint this workspace posts to, or one the outside world posts to.
Needs platform:manage · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/actions/platform.create_webhookInput
| Field | Type | Required | Notes |
|---|---|---|---|
direction | string (enum) | yes | Direction · Allowed values: outbound, inbound |
name | string | yes | Name · max 120 |
target_url | string | URL · Outbound only. · max 2000 | |
path | string | Path · Inbound only. The last segment of the address they post to. · max 80 | |
events | string | Events · Comma-separated. Leave empty for every event. · max 1000 | |
secret_hash | string | Signing secret digest · Inbound only: SHA-256 of the shared secret used to verify signatures. · max 64 | |
credential_id | uuid | Credential · Outbound only: the credential holding the signing secret. | |
timeout_ms | number | Timeout (ms) · Outbound only. How long to wait for the receiver. · min 1000 · max 30000 | |
max_attempts | number | Attempts · Outbound only. Tries before a delivery is given up on. · min 1 · max 10 | |
rate_limit_per_minute | number | Requests per minute · Inbound only. Per caller address; 0 means unlimited. · min 0 · max 6000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
address_missing | An outbound webhook needs a URL and an inbound one needs a path. |
invalid_url | That does not look like a web address. |
insecure_url | The address must use https. |
invalid_path | The path may contain only lowercase letters, numbers, hyphens and underscores. |
path_taken | Another endpoint already answers on that path. |
name_taken | This workspace already has a webhook with that name. |
not_a_digest | The signing secret digest must be a SHA-256 hash, not the secret itself. |
unsigned_inbound | An inbound webhook must carry a signing secret. |
Events it publishes: platform.webhook_created
What it writes: webhook (insert) — Registers the endpoint, inactive failures counted from zero
curl -X POST https://app.getera.io/api/v1/actions/platform.create_webhook \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"direction":"outbound","name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"platform.webhook_created"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.create_webhook", {
direction: "outbound",
name: "Name"
})era platform create_webhook \
--direction "outbound" \
--name "Name"{
"name": "era__platform__create_webhook",
"arguments": {
"direction": "outbound",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 address_missing, invalid_url, insecure_url, invalid_path, path_taken, name_taken, not_a_digest, unsigned_inbound · 429 slow down
platform.delete_webhook
Remove a webhook — Removes the endpoint and its delivery history.
Needs platform:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/actions/platform.delete_webhookInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
Events it publishes: platform.webhook_deleted
What it writes: webhook (delete) — Removes the endpoint; its deliveries cascade
curl -X POST https://app.getera.io/api/v1/actions/platform.delete_webhook \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.webhook_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.delete_webhook", {
webhook_id: "WEBHOOK_ID"
})era platform delete_webhook \
--webhook-id "WEBHOOK_ID"{
"name": "era__platform__delete_webhook",
"arguments": {
"webhook_id": "WEBHOOK_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found · 429 slow down
platform.end_session_key
End an Era session key — Revokes the short-lived key Era’s engine used for one run, as soon as that run ends.
Needs platform:read · changes data
403).https://app.getera.io/api/v1/actions/platform.end_session_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_id | uuid | yes | Session key |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
session_key_not_found | No live Era session key with that id in this workspace. |
not_your_session_key | Only the person an Era session key acts for can end it, and only a session key. |
Events it publishes: platform.key_revoked
What it writes: api_key (update) — Stamps the session key revoked
Errors: 401 no or expired credential · 403 the credential does not hold platform:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 session_key_not_found, not_your_session_key · 429 slow down
platform.enqueue_job
Queue work — Adds a job for a worker to claim. Re-queuing with the same key returns the first job.
Needs platform:handle · changes data
https://app.getera.io/api/v1/actions/platform.enqueue_jobInput
| Field | Type | Required | Notes |
|---|---|---|---|
type | string | yes | Type · max 80 |
payload | string | Payload · JSON. Large for an import job (crm.import_leads carries its rows). · max 20000000 | |
run_at | date-time | Run at · Leave empty to run as soon as a worker is free. | |
max_attempts | number | Attempts · min 1 · max 20 | |
idempotency_key | string | Idempotency key · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_payload | The payload must be a JSON object. |
queue_full | This workspace already has the maximum number of queued jobs. |
Events it publishes: platform.job_enqueued
What it writes: job (insert) — Adds the job, pending, due at run_at
curl -X POST https://app.getera.io/api/v1/actions/platform.enqueue_job \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"type":"Type"}'{
"ok": true,
"result": "…",
"events": [
"platform.job_enqueued"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.enqueue_job", {
type: "Type"
})era platform enqueue_job \
--type "Type"{
"name": "era__platform__enqueue_job",
"arguments": {
"type": "Type"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_payload, queue_full · 429 slow down
platform.issue_session_key
Start an Era session key — A short-lived key that lets Era’s engine use Era’s tools as you while it works on your request.
Needs platform:read · changes data
403).https://app.getera.io/api/v1/actions/platform.issue_session_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_hash | string | yes | Key digest · max 64 |
key_prefix | string | yes | Key prefix · max 16 |
expires_at | date-time | yes | Expires |
parent_key_id | uuid | Issued under · Your paired computer (or Era engine) key; revoking it revokes this one. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_a_digest | The key digest must be a SHA-256 hash, not the key itself. |
session_too_long | A session key must expire in the future, within six hours. |
parent_key_not_found | The key it is issued under must be your own live computer or Era engine key in this workspace. |
Events it publishes: platform.key_created
What it writes: api_key (insert) — Stores the session key’s digest, scoped to Era sessions and this person
Errors: 401 no or expired credential · 403 the credential does not hold platform:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_a_digest, session_too_long, parent_key_not_found · 429 slow down
platform.issue_station_key
Start Era’s engine for me — A key Era’s own engine uses to answer your chats as you, while your computer is off.
Needs platform:read · changes data
403).https://app.getera.io/api/v1/actions/platform.issue_station_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_hash | string | yes | Key digest · max 64 |
key_prefix | string | yes | Key prefix · max 16 |
expires_at | date-time | yes | Expires |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_a_digest | The key digest must be a SHA-256 hash, not the key itself. |
station_key_too_long | An engine key must expire in the future, within eight days. |
Events it publishes: platform.key_created, platform.key_revoked
What it writes: api_key (insert) — Stores the engine key’s digest, scoped to Era’s engine and this person; api_key (update) — Revokes the person’s earlier engine keys
Errors: 401 no or expired credential · 403 the credential does not hold platform:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_a_digest, station_key_too_long · 429 slow down
platform.pair_device
Pair my computer — Connects your own computer (Era Desktop or your cloud computer) to Era so your subscriptions and tasks can run on it.
Needs platform:read · changes data
403).https://app.getera.io/api/v1/actions/platform.pair_deviceInput
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Computer · max 120 |
key_hash | string | yes | Key digest · max 64 |
key_prefix | string | yes | Key prefix · max 16 |
replaces | string | Replaces · Your earlier pairing of the same computer, by name. · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_a_digest | The key digest must be a SHA-256 hash, not the key itself. |
device_limit_reached | You already have ten computers paired. |
computer_access_required | Only the workspace owner, or a member given computer access, can reach a computer. |
Events it publishes: platform.key_created, platform.key_revoked
What it writes: api_key (insert) — Stores the device key’s digest, scoped to this computer and this person; api_key (update) — Revokes the person’s earlier pairing of the same computer
Errors: 401 no or expired credential · 403 the credential does not hold platform:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_a_digest, device_limit_reached, computer_access_required · 429 slow down
platform.purge_history
Sweep delivery history — Removes delivery records older than the retention window, and expired idempotency keys.
Needs platform:delete · removes or cannot be undone
https://app.getera.io/api/v1/actions/platform.purge_historyInput
| Field | Type | Required | Notes |
|---|---|---|---|
days | number | Keep for (days) · max 3650 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
retention_too_short | Retention must be at least one day. |
Events it publishes: platform.history_purged
What it writes: webhook_delivery (delete) — Removes delivery attempts older than the window; idempotency_key (delete) — Removes keys whose window has passed
curl -X POST https://app.getera.io/api/v1/actions/platform.purge_history \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"days":1}'{
"ok": true,
"result": "…",
"events": [
"platform.history_purged"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.purge_history", {
days: 1
})era platform purge_history \
--days 1{
"name": "era__platform__purge_history",
"arguments": {
"days": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 retention_too_short · 429 slow down
platform.record_delivery
Record a webhook delivery — One row per attempt, with the response and how long it took.
Needs platform:handle · changes data
https://app.getera.io/api/v1/actions/platform.record_deliveryInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
event_name | string | yes | Event · max 120 |
success | boolean | yes | Succeeded |
attempt | number | Attempt · min 1 · max 100 | |
idempotency_key | string | Idempotency key · max 200 | |
response_status | number | Response status · min 100 · max 599 | |
response_body | string | Response body · max 8000 | |
error_message | string | Error · max 2000 | |
duration_ms | number | Duration (ms) · min 0 | |
request_body | string | Body sent · JSON. Kept so the attempt can be replayed byte for byte. · max 262144 | |
event_id | string | Event · max 64 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
duplicate_attempt | That attempt has already been recorded. |
Events it publishes: platform.delivery_recorded, platform.webhook_disabled
What it writes: webhook_delivery (insert) — Writes the attempt; webhook (update) — Moves the consecutive-failure count, and disables the endpoint when it crosses the threshold
curl -X POST https://app.getera.io/api/v1/actions/platform.record_delivery \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID","event_name":"Event","success":true}'{
"ok": true,
"result": "…",
"events": [
"platform.delivery_recorded",
"platform.webhook_disabled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.record_delivery", {
webhook_id: "WEBHOOK_ID",
event_name: "Event",
success: true
})era platform record_delivery \
--webhook-id "WEBHOOK_ID" \
--event-name "Event" \
--success true{
"name": "era__platform__record_delivery",
"arguments": {
"webhook_id": "WEBHOOK_ID",
"event_name": "Event",
"success": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found, duplicate_attempt · 429 slow down
platform.rename_api_key
Rename an API key — The name is the only thing that identifies a key once it has been issued.
Needs platform:manage · changes data
https://app.getera.io/api/v1/actions/platform.rename_api_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_id | uuid | yes | Key |
name | string | yes | Name · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
key_not_found | No such key in this workspace. |
name_taken | This workspace already has a key with that name. |
already_revoked | A revoked key cannot be renamed — its name is part of the record. |
Events it publishes: platform.key_renamed
What it writes: api_key (update) — Changes the key’s name
curl -X POST https://app.getera.io/api/v1/actions/platform.rename_api_key \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"key_id":"KEY_ID","name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"platform.key_renamed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.rename_api_key", {
key_id: "KEY_ID",
name: "Name"
})era platform rename_api_key \
--key-id "KEY_ID" \
--name "Name"{
"name": "era__platform__rename_api_key",
"arguments": {
"key_id": "KEY_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 key_not_found, name_taken, already_revoked · 429 slow down
platform.retry_job
Retry a job — Puts a failed job back on the queue, due now.
Needs platform:manage · changes data
https://app.getera.io/api/v1/actions/platform.retry_jobInput
| Field | Type | Required | Notes |
|---|---|---|---|
job_id | uuid | yes | Job |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
job_not_found | No such job in this workspace. |
job_not_failed | Only a failed job can be retried. |
attempts_exhausted | That job has already used all of its attempts. |
Events it publishes: platform.job_retried
What it writes: job (update) — Returns the job to pending, due now, and raises its attempt ceiling by one
curl -X POST https://app.getera.io/api/v1/actions/platform.retry_job \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"job_id":"JOB_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.job_retried"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.retry_job", {
job_id: "JOB_ID"
})era platform retry_job \
--job-id "JOB_ID"{
"name": "era__platform__retry_job",
"arguments": {
"job_id": "JOB_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 job_not_found, job_not_failed, attempts_exhausted · 429 slow down
platform.revoke_api_key
Revoke an API key — Stops the key working immediately. The record stays so old requests can still be explained.
Needs platform:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/actions/platform.revoke_api_keyInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_id | uuid | yes | Key |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
key_not_found | No such key in this workspace. |
already_revoked | That key was already revoked. |
Events it publishes: platform.key_revoked
What it writes: api_key (update) — Stamps the key revoked, and by whom
curl -X POST https://app.getera.io/api/v1/actions/platform.revoke_api_key \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"key_id":"KEY_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.key_revoked"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.revoke_api_key", {
key_id: "KEY_ID"
})era platform revoke_api_key \
--key-id "KEY_ID"{
"name": "era__platform__revoke_api_key",
"arguments": {
"key_id": "KEY_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 key_not_found, already_revoked · 429 slow down
platform.rotate_webhook_secret
Rotate a signing secret — Issues a new secret. The old one keeps working for 24 hours.
Needs platform:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/actions/platform.rotate_webhook_secretInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
secret_hash | string | New secret digest · Inbound only: SHA-256 of the new shared secret. · max 64 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
digest_required | An inbound endpoint is rotated by sending the digest of its new secret. |
not_a_digest | The signing secret digest must be a SHA-256 hash, not the secret itself. |
Events it publishes: platform.webhook_secret_rotated
What it writes: webhook (update) — Moves the current secret to the overlap slot and stores the new one
curl -X POST https://app.getera.io/api/v1/actions/platform.rotate_webhook_secret \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.webhook_secret_rotated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.rotate_webhook_secret", {
webhook_id: "WEBHOOK_ID"
})era platform rotate_webhook_secret \
--webhook-id "WEBHOOK_ID"{
"name": "era__platform__rotate_webhook_secret",
"arguments": {
"webhook_id": "WEBHOOK_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found, digest_required, not_a_digest · 429 slow down
platform.send_test_delivery
Send a test or replay a delivery — Queues a signed delivery to the endpoint. With replay_of, resends that attempt’s exact body.
Needs platform:manage · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/actions/platform.send_test_deliveryInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
replay_of | uuid | Delivery to replay · Leave empty to send a test event. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
not_outbound | Only an endpoint this workspace calls can be sent a delivery. |
webhook_inactive | The endpoint is switched off. |
nothing_to_replay | That delivery kept no body to resend. |
Events it publishes: platform.test_delivery_queued
What it writes: job (insert) — Queues an apps.deliver job for the endpoint
curl -X POST https://app.getera.io/api/v1/actions/platform.send_test_delivery \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.test_delivery_queued"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.send_test_delivery", {
webhook_id: "WEBHOOK_ID"
})era platform send_test_delivery \
--webhook-id "WEBHOOK_ID"{
"name": "era__platform__send_test_delivery",
"arguments": {
"webhook_id": "WEBHOOK_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found, not_outbound, webhook_inactive, nothing_to_replay · 429 slow down
platform.set_webhook_active
Switch a webhook on or off — Switching one back on clears the failure count, so the disable threshold starts again.
Needs platform:manage · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/actions/platform.set_webhook_activeInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
active | boolean | yes | Active |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
no_change | The webhook is already in that state. |
Events it publishes: platform.webhook_enabled, platform.webhook_disabled
What it writes: webhook (update) — Flips the active flag and, when enabling, resets the failure count
curl -X POST https://app.getera.io/api/v1/actions/platform.set_webhook_active \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID","active":true}'{
"ok": true,
"result": "…",
"events": [
"platform.webhook_enabled",
"platform.webhook_disabled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.set_webhook_active", {
webhook_id: "WEBHOOK_ID",
active: true
})era platform set_webhook_active \
--webhook-id "WEBHOOK_ID" \
--active true{
"name": "era__platform__set_webhook_active",
"arguments": {
"webhook_id": "WEBHOOK_ID",
"active": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found, no_change · 429 slow down
platform.unpair_device
Remove my computer — Disconnects one of your own computers from Era. Its sign-ins stay on that computer.
Needs platform:read · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/actions/platform.unpair_deviceInput
| Field | Type | Required | Notes |
|---|---|---|---|
key_id | uuid | yes | Computer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
key_not_found | No such computer in this workspace. |
not_your_device | Only the person who paired a computer can remove it here. |
Events it publishes: platform.key_revoked
What it writes: api_key (update) — Stamps the device key revoked, and by whom
curl -X POST https://app.getera.io/api/v1/actions/platform.unpair_device \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"key_id":"KEY_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.key_revoked"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.unpair_device", {
key_id: "KEY_ID"
})era platform unpair_device \
--key-id "KEY_ID"{
"name": "era__platform__unpair_device",
"arguments": {
"key_id": "KEY_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 key_not_found, not_your_device · 429 slow down
platform.update_webhook
Edit a webhook — Name, address, events, timeout, attempts and inbound rate limit.
Needs platform:manage · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/actions/platform.update_webhookInput
| Field | Type | Required | Notes |
|---|---|---|---|
webhook_id | uuid | yes | Webhook |
name | string | Name · max 120 | |
target_url | string | URL · Outbound only. · max 2000 | |
events | string | Events · Comma-separated. Empty means every event. · max 1000 | |
timeout_ms | number | Timeout (ms) · min 1000 · max 30000 | |
max_attempts | number | Attempts · min 1 · max 10 | |
rate_limit_per_minute | number | Requests per minute · min 0 · max 6000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
webhook_not_found | No such webhook in this workspace. |
name_taken | This workspace already has a webhook with that name. |
invalid_url | That does not look like a web address. |
insecure_url | The address must use https. |
Events it publishes: platform.webhook_updated
What it writes: webhook (update) — Rewrites the endpoint’s settings
curl -X POST https://app.getera.io/api/v1/actions/platform.update_webhook \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"webhook_id":"WEBHOOK_ID"}'{
"ok": true,
"result": "…",
"events": [
"platform.webhook_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("platform.update_webhook", {
webhook_id: "WEBHOOK_ID"
})era platform update_webhook \
--webhook-id "WEBHOOK_ID"{
"name": "era__platform__update_webhook",
"arguments": {
"webhook_id": "WEBHOOK_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold platform:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 webhook_not_found, name_taken, invalid_url, insecure_url · 429 slow down
Data models
EventSubscriberSkip
Table event_subscriber_skip · Resource event_subscriber_skips
GET /api/v1/event_subscriber_skipsGET /api/v1/event_subscriber_skips/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
event_id | string (int64) · int8 | event | ||
subscriber | string · text | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
reason | string · text | |||
skipped_at | string (date-time) · timestamptz |
Primary key: event_id, subscriber
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
platform.delivery_recorded | A webhook delivery attempt finished. | webhook_id, success, response_status |
platform.history_purged | Delivery history and expired idempotency keys were swept. | days, removed |
platform.job_cancelled | A queued job was cancelled. | job_id |
platform.job_enqueued | Work was put on the queue. | job_id, type |
platform.job_retried | A failed job was put back on the queue. | job_id |
platform.key_created | An API key was issued. | key_id, prefix |
platform.key_renamed | An API key was renamed. | key_id, name |
platform.key_revoked | An API key was revoked. | key_id, prefix |
platform.test_delivery_queued | A test or replayed delivery was put on the queue. | webhook_id, job_id, replay_of |
platform.webhook_created | A webhook endpoint was registered. | webhook_id, direction |
platform.webhook_deleted | A webhook endpoint was removed. | webhook_id |
platform.webhook_disabled | A webhook endpoint was switched off. | webhook_id, reason |
platform.webhook_enabled | A webhook endpoint was switched back on. | webhook_id |
platform.webhook_received | An inbound webhook was accepted. | webhook_id, path, name, body |
platform.webhook_secret_rotated | A webhook signing secret was replaced; the old one keeps working for the overlap window. | webhook_id, overlap_until |
platform.webhook_updated | A webhook endpoint’s address, events or limits changed. | webhook_id |