Card Sync Flowchart
Mirror card creates, updates, and archives from Guru's webhooks into any external tool.
When a card changes in Guru, Guru's webhooks emit an event to your endpoint. Your integration then makes the API calls needed to mirror that change in an external tool, whether that's Salesforce, Zendesk, Confluence, or any other system with a REST API. The three flows below cover the full lifecycle of a card.
The webhook payload
Every card event arrives in the same envelope; only the event type changes. This example shows a card-created event with the webhook's deliveryMode set to BATCH, the recommended mode:
{
"items": [
{
"webhookId": "RVxvFQ",
"source": "channel.message",
"serial": "01788968371604-000@6f8GnB8uAC6d0U81532972",
"timestamp": 1788968371604,
"name": "channel.message",
"data": {
"channelId": "wh:f13d7e8a-b011-4b99-9071-bc2800238945",
"site": "us-west-1-A",
"messages": [
{
"id": "3e276ee9-857a-4ec6-993d-36c18ff32d72:0",
"timestamp": 1788968371604,
"data": "{\"user\":\"[email protected]\",\"properties\":{\"cardId\":\"1bd32498-9672-4663-ab80-c0931db6d976\",\"source\":\"UI\",\"collectionId\":\"796ea6f8-6132-4202-a1db-64635f1cf2e7\"},\"id\":\"dc5d2df3-eb59-4276-b991-5523ea8cb20a\",\"eventType\":\"card-created\",\"eventDate\":\"2026-09-09T15:39:29.891+0000\"}",
"action": 0,
"serial": "01788968371604-000@6f8GnB8uAC6d0U81532972:000",
"name": "card-created"
}
]
}
}
]
}
Parse thedatafield twiceEach message's
datafield is a JSON-encoded string, not a nested object. Parse the request body first, then parse each message'sdatato get the event. A single envelope can also carry multiple messages, so iterate overmessagesrather than assuming one event per delivery.
import json
envelope = json.loads(request_body)
for item in envelope["items"]:
for message in item["data"]["messages"]:
event = json.loads(message["data"])
print(event["eventType"], event["properties"]["cardId"])The parsed event looks like this (this is also exactly what a SINGLE delivery mode request body looks like):
{
"user": "[email protected]",
"properties": {
"cardId": "1bd32498-9672-4663-ab80-c0931db6d976",
"source": "UI",
"collectionId": "796ea6f8-6132-4202-a1db-64635f1cf2e7"
},
"id": "dc5d2df3-eb59-4276-b991-5523ea8cb20a",
"eventType": "card-created",
"eventDate": "2026-09-09T15:39:29.891+0000"
}| Field | Description |
|---|---|
user | Email address of the user who performed the action. |
properties.cardId | ID of the card the event refers to. Use it to fetch the card or look up the external record. |
properties.source | Where the action originated, for example UI. |
properties.collectionId | ID of the collection the card belongs to. |
id | Unique ID of the event. |
eventType | The event name: card-created, card-updated, or card-deleted. |
eventDate | ISO 8601 timestamp of when the event occurred. |
If your webhook's deliveryMode is SINGLE instead, there is no envelope: the request body is exactly the event object shown above, one request per event, with no second parse needed. BATCH is recommended because it delivers events more efficiently at higher frequencies, while SINGLE can cause delivery backups when many card events fire in a short window. See Creating a Webhook for details.
Sync only one collection
Webhooks fire for card events across your whole team; there is no way to scope a webhook to a single collection in Guru. If you only want to sync cards from a particular collection, filter on your side using the properties.collectionId field in each event before making any API calls:
TARGET_COLLECTION = "796ea6f8-6132-4202-a1db-64635f1cf2e7"
event = json.loads(message["data"])
if event["properties"].get("collectionId") != TARGET_COLLECTION:
continue # card belongs to a different collection, skip itFor archive events, the lookup step in the flow below doubles as a filter: if no external record has the incoming card ID stored on it, the card was never synced, so there is nothing to remove.
Filtering by collection also means deciding what happens when a card moves out of the synced collection. Once it moves, your filter skips its events, so the record already created in the external tool is not cleaned up automatically. If cards move between collections on your team, plan for that case, for example by periodically reconciling the external records against the collection's current cards.
When a card is created
%%{init: {"theme": "base", "themeVariables": {"fontSize": "16px", "lineColor": "#9C9C9C"}, "flowchart": {"nodeSpacing": 40, "rankSpacing": 45, "padding": 12}}}%%
flowchart TB
W1(["Guru emits<br/>card-created webhook"])
A1["Your endpoint receives<br/>the event payload"]
B1["Get full card details<br/>from the Guru API"]
C1["POST a new record<br/>to the external tool"]
Z1["Find the external record by its stored Guru card ID"]
W1 --> A1 --> B1 --> C1
click B1 "https://developer.getguru.com/reference/getv1cardsgetextendedfact" "Get card endpoint reference"
classDef webhook fill:#56D886,stroke:#56D886,color:#080B0E
classDef guru fill:#F0F0F0,stroke:#9C9C9C,color:#080B0E,rx:8,ry:8
classDef external fill:#080B0E,stroke:#080B0E,color:#FFFFFF,rx:8,ry:8
classDef spacer fill:none,stroke:none,color:transparent
class W1 webhook
class A1,B1 guru
class C1 external
class Z1 spacer
Use Get card to fetch the full card content, then store the Guru card ID on the external record when you create it. The update and archive flows depend on this reference to find the right record later. In Salesforce this might be a custom field like guru_card_id__c; in other tools, use whatever custom field or metadata mechanism is available.
When a card is updated
%%{init: {"theme": "base", "themeVariables": {"fontSize": "16px", "lineColor": "#9C9C9C"}, "flowchart": {"nodeSpacing": 40, "rankSpacing": 45, "padding": 12}}}%%
flowchart TB
W2(["Guru emits<br/>card-updated webhook"])
A2["Your endpoint receives<br/>the event payload"]
B2["Get full card details<br/>from the Guru API"]
C2["Find the external record<br/>by its stored Guru card ID"]
D2["PATCH the record<br/>in the external tool"]
Z2["Find the external record by its stored Guru card ID"]
W2 --> A2 --> B2 --> C2 --> D2
click B2 "https://developer.getguru.com/reference/getv1cardsgetextendedfact" "Get card endpoint reference"
classDef webhook fill:#56D886,stroke:#56D886,color:#080B0E
classDef guru fill:#F0F0F0,stroke:#9C9C9C,color:#080B0E,rx:8,ry:8
classDef external fill:#080B0E,stroke:#080B0E,color:#FFFFFF,rx:8,ry:8
classDef spacer fill:none,stroke:none,color:transparent
class W2 webhook
class A2,B2 guru
class C2,D2 external
class Z2 spacer
Fetch the updated content with Get card, then search the external tool for the record whose stored Guru card ID matches the properties.cardId in the event and apply the changes.
When a card is archived
Archiving is Guru's removal actionGuru emits the
card-deletedevent when a card is archived in Guru. Treat this event as the signal to remove the corresponding record from the external tool.
%%{init: {"theme": "base", "themeVariables": {"fontSize": "16px", "lineColor": "#9C9C9C"}, "flowchart": {"nodeSpacing": 40, "rankSpacing": 45, "padding": 12}}}%%
flowchart TB
W3(["Guru emits<br/>card-deleted webhook"])
A3["Your endpoint receives<br/>the event payload"]
B3["Find the external record<br/>by its stored Guru card ID"]
C3["DELETE the record<br/>in the external tool"]
Z3["Find the external record by its stored Guru card ID"]
W3 --> A3 --> B3 --> C3
classDef webhook fill:#56D886,stroke:#56D886,color:#080B0E
classDef guru fill:#F0F0F0,stroke:#9C9C9C,color:#080B0E,rx:8,ry:8
classDef external fill:#080B0E,stroke:#080B0E,color:#FFFFFF,rx:8,ry:8
classDef spacer fill:none,stroke:none,color:transparent
class W3 webhook
class A3,B3 guru
class C3 external
class Z3 spacer
No call to the Guru API is needed here, since the card is no longer available. The event's properties.cardId is enough to find and remove the external record. If your destination tool supports archiving instead of hard deletion, you may prefer that to preserve history and mirror Guru's own behavior.
Implementation notes
- The webhook event contains the card ID (
properties.cardId) but not the full card content, which is why the create and update flows call Get card before writing to the external tool. - Configure which events your webhook receives with the
filterfield when creating the webhook, for examplecard-created,card-updated,card-deleted. - The
filterfield on a webhook scopes which event types are delivered, not which collections. To sync a single collection, filter onproperties.collectionIdin your receiver as described above. - In the diagrams above, green is the webhook Guru emits, grey steps interact with Guru, and black steps call the external tool's API.
Updated 1 day ago

