project.resource.updated
A resource's title, description or on/off switch changes, or a recovery moves it onto another place.
There are no project.resource.activated / deactivated events. Switching a
resource on or off arrives here, as changes.active, alongside title and
description edits. One save is one event, and it lists only what changed.
When this fires
A creator changes one of the three editable fields on a resource — title, description or active — and at least one of them is actually different afterwards. Every surface that can make that change raises it:
- Dashboard — the resource editor on the project's Resources page, and the on/off switch on each resource row.
- REST —
PATCH /v1/projects/{project}/resources/{resource}for title and description edits, andPOST /v1/projects/{project}/resources/{resource}/activate/deactivatefor the switch. - MCP — the
update_resource,activate_resourceanddeactivate_resourcetools.
It also fires when the place behind a resource is swapped: a Disaster Recovery replacement, an automatic failover to a standby, Swap & Grant Resource from the resources list, or the undo of any of those. That event carries changes.space_id with the previous and the new space, changes.kind when the platform upgraded the place in the same move (a Telegram group that became a supergroup), and resource.space already reads the new place.
It does not fire when:
- Nothing changed. A
PATCHthat re-sends the stored values, or anactivatecall on a resource that is already active, writes nothing and emits nothing. - The place is linked for the first time or removed. That is the connector's link (
project.resource.linked) or an unlink (project.resource.unlinked). - The resource is deleted. That is
project.resource.deleted, and noupdatedevent precedes it.
Required ability
project-resource:view — token must carry this at mint time to subscribe an endpoint to this event.
Payload
{
"id": "evt_01HX...",
"type": "project.resource.updated",
"created_at": "2026-09-06T09:14:00Z",
"api_version": "2026-05-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"resource": {
"id": "b73c5f21-9d80-4a6e-8215-4f70ce13a9d6",
"title": "Members lounge (VIP)",
"kind": "telegram:supergroup",
"connector": "telegram",
"space": {
"id": "3c1f7a58-2b6e-4d90-8f41-6a2e9c5d7b10",
"external_id": "-1001234567890",
"title": "Members lounge"
},
"active": true
},
"changes": {
"title": {
"from": "Members lounge",
"to": "Members lounge (VIP)"
}
}
}
}Field reference
| Field | Type | Notes |
|---|---|---|
id | string ULID | Unique event id, prefixed evt_. Use for idempotent processing. |
type | string | Always project.resource.updated for this event. |
created_at | ISO 8601 timestamp | Server-side emission time. |
api_version | string | Webhook API contract version. |
project_id | string UUID | Project this event belongs to. |
data.resource | object | Post-update snapshot — id, title, kind, connector, space, active only. |
data.resource.id | string UUID | Resource identifier. |
data.resource.title | string | Display title after the change. |
data.resource.kind | string | manual, or the connector's kind spelled connector:kind. Shown so a consumer can branch without a lookup; changes only when a swap upgraded the place, and then changes.kind says so. |
data.resource.connector | string, nullable | The connector the place is on; null for a manual perk. |
data.resource.space | object, nullable | The place after the change: id, the platform's external_id, title. null for a manual perk and for an unlinked row. |
data.resource.active | boolean | Switch position after the change. |
data.changes | object<string, {from,to}> | Map of the fields that changed. Keys are one or more of title, description, active, space_id, kind; values are before/after pairs. Never empty — an empty map means no event. |
data.changes.title.from | string | Previous title. Present only when the title changed. |
data.changes.title.to | string | New title. |
data.changes.description.from | string, nullable | Previous description. Present only when the description changed. null when there was none. |
data.changes.description.to | string, nullable | New description — up to 1000 characters of well-formed HTML, or null when it was cleared. |
data.changes.active.from | boolean | Previous switch position. Present only when the switch flipped. |
data.changes.active.to | boolean | New switch position. |
data.changes.space_id.from | string UUID, nullable | The space the resource pointed at before a swap. Present only when a recovery, a failover, a manual swap or an undo moved the place. |
data.changes.space_id.to | string UUID, nullable | The space it points at now; resource.space.id reads the same value. |
data.changes.kind.from | string | The kind before a swap that upgraded the place. Present only then. |
data.changes.kind.to | string | The kind after it (telegram:supergroup for a group Telegram converted). |
Caveats
- The
descriptionis not in theresourcesnapshot, matching every otherproject.resource.*event — but when the description is what changed,changes.description.tocarries the new text in full. Read it from there rather than following up withGET /v1/projects/{project}/resources/{resource}. - Switching a resource off does not touch the plans that sell it. The connector stops granting that one place while the plans keep selling everything else, so a
changes.activeflip tofalseis "this perk is paused", not "this plan changed". Noplan.updatedfollows. - An
activate/deactivatecall and aPATCHthat setsactiveproduce the same event with the samechanges.activeshape. The payload does not say which route was used. - A swap that also switches an inactive resource back on carries
changes.activebesidechanges.space_id, because the swap reactivates what it repairs. changescontains only the fields that mutated. APATCHcarrying all three fields where only one differs reports one key.
Related events
project.resource.created— the manual perk this event later edits.project.resource.linked/project.resource.unlinked— the place bound for the first time or removed, which this event never reports.project.resource.deleted— terminal state for a resource.project.resource.status_changed— the health transition that usually precedes a recovery swap.recovery.resource_replaced/recovery.resource_failed_over— the recovery ledger's own word for the same swap.- Project events overview — back to family overview.
How is this guide?