> 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.

You can have Polytomic Connect sync updates from your sources to [custom webhooks](https://apidocs.polytomic.com/guides/configuring-your-connections/connections/webhooks). You can do this by creating a Model Sync that maps fields from a Polytomic Model to your webhook destination.

## Code example

When creating a Model Sync to a custom webhook destination, set each field's `target` to `record`. Additionally, the `target.object` must be `http` and the sync mode is always `updateOrCreate`.

The below request creates a Model Sync to a custom webhook:

#### cURL

```bash
curl --request POST  \
     --url https://app.polytomic.com/api/syncs \
     --header "accept: application/json"  \
     --header "content-type: application/json"  \
     --header "Authorization: Bearer ${POLYTOMIC_API_KEY}"  \
     -d '{
          "name": "Sync to Webhook",
          "active": true,
          "mode": "updateOrCreate",
          "fields": [
               {
                    "source": {
                         "field": "field1",
                         "model_id": "YOUR_MODEL_ID"
                    },
                    "target": "record"
               },
               {
                    "source": {
                         "field": "field2",
                         "model_id": "YOUR_MODEL_ID"
                    },
                    "target": "record"
               },
               {
                    "source": {
                         "field": "field3",
                         "model_id": "YOUR_MODEL_ID"
                    },
                    "target": "record"
               }
          ],
          "target": {
               "connection_id": "YOUR_WEBHOOK_CONNECTION_ID",
               "object": "http",
               "configuration": {
                    "batch_size": 100,
                    "record_requests": true
               }
          },
          "schedule": {
               "frequency": "manual"
          }
     }'
```

#### TypeScript

```typescript
import { Polytomic, PolytomicClient } from 'polytomic';

const polytomic = new PolytomicClient({
  token: "POLYTOMIC_API_KEY",
});

polytomic.modelSync.create({
  name: "Sync to Webhook",
  mode: "updateOrCreate",
  active: true,
  fields: [
    {
      source: {
        field: "field1",
        model_id: "YOUR_MODEL_ID"
      },
      target: "record"
    },
    {
      source: {
        field: "field2",
        model_id: "YOUR_MODEL_ID"
      },
      target: "record"
    },
    {
      source: {
        field: "field3",
        model_id: "YOUR_MODEL_ID"
      },
      target: "record"
    }
  ],
  schedule: {
    frequency: Polytomic.ScheduleFrequency.Manual
  },
  target: {
    connection_id: "YOUR_WEBHOOK_CONNECTION_ID",
    object: "http",
    configuration: {
        batch_size: 100,
        record_requests: true
    }
  }
}).then((resp) => {
  console.log(resp);
})
```

#### Python

```python
import os
from polytomic.client import Polytomic
from polytomic import ModelSyncField, Source, Identity, Schedule, ScheduleFrequency, Target

client = Polytomic(
    token=os.getenv("POLYTOMIC_API_KEY"),
)

resp = client.model_sync.create(
    name="Sync to Webhook",
    active="true",
    mode="updateOrCreate",
    fields=[
        ModelSyncField(
            source=Source(field="field1", model_id="YOUR_MODEL_ID"),
            target="record",
        ),
        ModelSyncField(
            source=Source(field="field2", model_id="YOUR_MODEL_ID"),
            target="record",
        ),
        ModelSyncField(
            source=Source(field="field3", model_id="YOUR_MODEL_ID"),
            target="record",
        ),
    ],
    target=Target(connection_id="YOUR_WEBHOOK_CONNECTION_ID", object="http", configuration={batch_size: 100, record_requests: true}),
    schedule=Schedule(
        frequency=ScheduleFrequency.MANUAL
    )
)

print(resp.data)
```

#### Go

```golang

import (
	"context"
	"fmt"

	polytomic "github.com/polytomic/polytomic-go"
	polytomicgoclient "github.com/polytomic/polytomic-go/client"
	"github.com/polytomic/polytomic-go/option"
)

client := polytomicgoclient.NewClient(
    option.WithToken(os.Getenv("POLYTOMIC_API_TOKEN")),
)

resp, err := client.ModelSync.Create(
    context.TODO(),
    &polytomic.CreateModelSyncRequest{
        Name: "Sync to Webhook",
        Active: true,
        Mode: "updateOrCreate",
        Fields: []*polytomic.ModelSyncField{
            {
                Source: &polytomic.Source{
                    Field:   "field1",
                    ModelId: "YOUR_MODEL_ID",
                },
                Target: "record",
            },
            {
                Source: &polytomic.Source{
                    Field:   "field2",
                    ModelId: "YOUR_MODEL_ID",
                },
                Target: "record",
            },
        },
        Target: &polytomic.Target{
            ConnectionId: "YOUR_WEBHOOK_CONNECTION_ID",
            Object:       "http",
            Configuration: map[string]any{
                "batch_size": 100,
                "record_requests": true
            }
        },
        Schedule: &polytomic.Schedule{
            Frequency: pointer.ToString(string(polytomic.ScheduleFrequencyManual)),
        }
    }
)

if err != nil {
    panic(err)
}
fmt.Println(resp)
```

## Payload format

Polytomic's payloads will arrive in this format:

```
POST /path/to/api HTTP/1.1
Host: mysite.com
Accept-Encoding: gzip
Content-Type: application/json
Polytomic-Signature-Timestamp: 2021-06-01T22:55:36Z
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3ZWJob29rIiwianRpIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIiwiaXNzIjoiaHR0cHM6Ly9hcHAucG9seXRvbWljLWxvY2FsLmNvbTo4NDQzLyJ9.FBSU_fC1YFyWhMSPErRono4BPfkIeT3MkRdZrepiP3c
Content-Length: 646
User-Agent: Polytomic/rel2021.05.25 

{
  "event": "sync.records",
  "object": {
    "id": "1ea8f90a-b22e-4218-86d5-c3c109e1fbb7",
    "name": "Webhook HTTP Endpoint sync",
    "records": [
      {
        "hash": "b7421c6c57bd49f7",
        "fields": {
          "email": "nathan@polytomic.com",
          "last_login": "2020-12-02T00:00:00Z"
        }
      },
			...
    ]
  }
}
```

## Payload explanation

### Headers

#### Accept-Encoding

Polytomic delivers payloads as a gzipped response to minimize bandwidth use. Your client likely supports decoding this automatically.

#### Content-Type

Polytomic delivers its webhooks payloads as json only. This header will always be present.

#### Polytomic-Signature-Timestamp

This signature lets your backend know when the request was created. In the future it may be used in combination with message signing to provide security. In general, it is a good idea to reject requests older than you expect (more than a few minutes old).

#### Authorization

This should be Bearer token matching the same value that was provided as the Secret during connection setup. For now, this is the only request authorization and is a static value.

### Body

```
{
  # This is an event type to help you distinguish new and future hooks.
  # You should only process webhooks you know about—for right now, that is
  # just the sync.records event.
  "event": "sync.records",
  
  # Object is an envelope that will contain the payload, regardless of event
  "object": {
  
  	# This is the ID of the sync that the webhook is for. It will match
    # the value seen the URL bar when you have the corresponding sync
    # configuration open.
    "id": "1ea8f90a-b22e-4218-86d5-c3c109e1fbb7",
    
    # This name matches the sync setup you created in Polytomic. It can be
    # useful for discriminating against data coming in from different endpoints.
    "name": "Webhook HTTP Endpoint sync",
    
    # This a list of the records changed since the last payload.
    "records": [
      {
        # hash is a computed hash of the record's fields key/values pairs, which 
        # may be useful for deduplicating incoming data.
        "hash": "b7421c6c57bd49f7",
        
        # Fields contains each of the fields you selected to be delivered.
        "fields": {
          "email": "nathan@polytomic.com",
          "last_login": "2020-12-02T00:00:00Z"
        }
      },
			...
    ],
    "metadata": {
      # Any key-value pairs of metadata defined in the sync configuration
    }
  }
}
```

### Field normalization

Polytomic will strip away characters from your field names that do not match any of these criteria:

* Alphanumeric
* Underscores
* Hyphens
* Periods or full stops

For example, if your source field is named `$first_name`, your webhook payload will contain the field `first_name` because the `$` character does not match any of the above criteria.

This normalization can be turned off if you desire.

## Whitelist IPs

Requests will arrive from Polytomic IP addresses. You may need to whitelist them if your webhook host is behind a firewall. You can find the list of Polytomic IP addresses [here](https://apidocs.polytomic.com/guides/ip-whitelisting).

## Payload API response

On receipt of the payload, your API should return `200 OK`. Any `4xx` or `5xx` error will cause Polytomic to declare the sync to have failed.