> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://apidocs.polytomic.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://apidocs.polytomic.com/_mcp/server.

Events notify you when important actions happen inside an organization.
For example, Polytomic emits a `sync.completed` event every time a sync
finishes.

You can consume events in two ways:

* Poll the [events endpoint](/api-reference/events/list).
* Register a webhook to receive events in real time.

> ⚠️ At-most-once delivery
>
> Polytomic delivers each event at most once and does not re-send events
> after delivery. If you need guaranteed processing of every change, build a
> fallback that polls the events endpoint in addition to consuming the
> webhook.

> **Retention**
>
> Polytomic retains events for 48 hours. After that they are no longer
> available from the events endpoint.

## Event types

The event types are as follows:

* `sync.running`
* `sync.failed`
* `sync.canceled`
* `sync.completed`
* `sync.completed_with_errors`
* `bulk_sync.running`
* `bulk_sync.completed`
* `bulk_sync.canceled`
* `bulk_sync.failed`
* `bulk_sync.completed_with_error`

## Webhooks

Create and manage webhooks through the webhook API endpoints. A webhook
fires events for the organization it belongs to. Each organization can
have one webhook.

## HMAC validation

Use HMAC validation to confirm that a delivered event came from
Polytomic. When you create or update a webhook, you supply a `secret`.
Polytomic signs each delivery with that secret and passes the signature in
the `Polytomic-Signature` header. Compute the same HMAC on the request body
and compare.

## Delivery

Your endpoint must return a `2xx` status code. If it does not, Polytomic
retries the delivery up to five times with exponential backoff. Event ordering
is not guaranteed.

## Record logs

The `sync.completed` event payload (see the example below) includes
links to JSON logs of the records Polytomic inserted or updated. See the
`total_records`, `inserted_records`, and `updated_records` fields.

## Event payload examples

```json
{
	"type": "bulk_sync.running",
	"event": {
		"name": "Asana to BigQuery sync",
		"organization_id": "be80a27e-0e80-4dcb-bee9-1666f02eeb83",
		"sync_id": "dcc891b3-4a25-4b8b-bba8-48104cd66525",
		"execution_id": "8114c8cc-99fc-4fb4-ab72-28308762fa63",
		"source_connection_id": "ad56197c-1bca-4256-a410-fb1ffde295c0",
		"destination_connection_id": "318dba62-d875-11ed-b59b-ea7534cffcab"
   }
}
```

\


```json
{
	"type": "bulk_sync.completed",
	"event": {
		"name": "Asana to BigQuery sync",
		"organization_id": "be80a27e-0e80-4dcb-bee9-1666f02eeb83",
		"sync_id": "dcc891b3-4a25-4b8b-bba8-48104cd66525",
		"execution_id": "8114c8cc-99fc-4fb4-ab72-28308762fa63",
		"source_connection_id": "ad56197c-1bca-4256-a410-fb1ffde295c0",
		"destination_connection_id": "318dba62-d875-11ed-b59b-ea7534cffcab"
  }
}
```

\


```json
{
	"type": "sync.running",
	"event": {
		"name": "Salesforce Sync",
		"organization_id": "be80a27e-0e80-4dcb-bee9-1666f02eeb83",
		"execution_id": "2a42c650-b741-4282-a4c7-19de797aa18b",
		"sync_id": "d09ceb2a-1641-4dc8-bdfe-d019d8964043",
		"target_connection_id": "7c67e0b3-9759-44eb-b96c-2a7042b583f0"
	}
}
```

\


```json
{
	"type": "sync.completed",
	"event": {
		"name": "Salesforce Sync",
		"organization_id": "be80a27e-0e80-4dcb-bee9-1666f02eeb83",
		"sync_id": "d09ceb2a-1641-4dc8-bdfe-d019d8964043",
		"execution_id": "2a42c650-b741-4282-a4c7-19de797aa18b",
		"status": "completed",
    "total_records": [
      "https://app.polytomic.com/api/syncs/54d2d580-d910-4bc7-834b-92d57ca89762/executions/c9fd1b24-0b00-46e8-905b-839f919adffd/records/log1688189538-0d74ec71-4540-4706-867f-e6263070e058.json"
    ],
    "inserted_records": null,
    "updated_records": [
      "https://app.polytomic.com/api/syncs/54d2d580-d910-4bc7-834b-92d57ca89762/executions/c9fd1b24-0b00-46e8-905b-839f919adffd/updates/log1688189542-05f04ffa-3c85-41eb-8333-9d951f93405b.json",
      "https://app.polytomic.com/api/syncs/54d2d580-d910-4bc7-834b-92d57ca89762/executions/c9fd1b24-0b00-46e8-905b-839f919adffd/updates/log1688189552-6c0bcf4a-b1ae-4cf5-8ee2-dcb6d6fbb728.json"
		"trigger": "manual",
		"target_connection_id": "7c67e0b3-9759-44eb-b96c-2a7042b583f0"
  }
}
```

## Consuming events

The following Go example receives a webhook delivery and verifies the
HMAC signature:

```go
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/gin"
)

var key = []byte("somepassword")

func main() {
	r := gin.Default()

	r.POST("/webhook", func(c *gin.Context) {
		body, _ := ioutil.ReadAll(c.Request.Body)
		hash := c.Request.Header.Get("Polytomic-Signature")
		sig, err := hex.DecodeString(hash)
		if err != nil {
			panic(err)
		}
		mac := hmac.New(sha256.New, key)
		mac.Write(body)

		if !hmac.Equal(sig, mac.Sum(nil)) {
			panic("Invalid signature")
		}

		fmt.Printf("Headers: %+v, Body: %s", c.Request.Header, string(body))
		c.Status(http.StatusOK)
	})

	r.Run("0.0.0.0:8000")
}
```