# Zapier

Create and schedule posts from a Zap with Webhooks by Zapier, and start Zaps from Pozy events with a signature check in Code by Zapier.

Zapier connects to Pozy through **Webhooks by Zapier**, which sends HTTP requests and catches incoming ones, and **Code by Zapier** for the signature check. Webhooks by Zapier is available on Zapier's paid plans.

You need a Pozy API key (**Developers → API keys** in the dashboard) and at least one connected account.

## Find your account ids

Before building the Zap, list your accounts once from a terminal:

```bash Terminal
curl https://api.pozy.ai/v1/accounts \
  -H "Authorization: Bearer $POZY_API_KEY"
```

Note the `_id` and `platform` of each account you want to post to. You will type them into the Zap.

## Create a post from a Zap

### Add the action

After your trigger (a new spreadsheet row, a new RSS item, a form entry), add **Webhooks by Zapier** and choose the **Custom Request** event. It lets you send a JSON body exactly as you write it.

### Fill in the request

| Field | Value |
| --- | --- |
| Method | POST |
| URL | `https://api.pozy.ai/v1/posts` |
| Data | The JSON body below, with fields from earlier steps inserted |
| Headers | `Authorization` = `Bearer pz_live_...`, `Content-Type` = `application/json`, `Idempotency-Key` = an id from the trigger, such as the row id |

```json
{
  "content": "New on the blog: {{title}} {{link}}",
  "platforms": [
    { "platform": "linkedin", "accountId": "6650f1c2a8b4e3d2c1b0a333" },
    { "platform": "twitter", "accountId": "6650f1c2a8b4e3d2c1b0a444" }
  ],
  "publishNow": true
}
```

Insert the trigger's fields where the placeholders are, using Zapier's field picker.

### Test the step

Zapier sends the request and shows Pozy's response. A created post has `post._id` and a status per platform. Switch `publishNow` to `"isDraft": true` while you test if you do not want anything to go live.

> **Text with quotes:**
> 
> A double quote or a line break inside an inserted field breaks the JSON and Pozy answers `400`. If your text can contain them, escape it first in a **Code by Zapier → Run JavaScript** step with `return { text: JSON.stringify(inputData.text).slice(1, -1) };`, and insert that step's `text` instead.

## Schedule for later

Use **Formatter by Zapier → Date / Time → Add/Subtract Time** to compute the date, then send a local time and a timezone:

```json
{
  "content": "{{text}}",
  "platforms": [{ "platform": "linkedin", "accountId": "6650f1c2a8b4e3d2c1b0a333" }],
  "scheduledFor": "{{formatted_date}}T09:00:00",
  "timezone": "America/New_York"
}
```

Set the Formatter's output format to `YYYY-MM-DD`. A time without an offset is read in `timezone`. See [Scheduling and timezones](https://pozy.ai/docs/publishing/scheduling).

## Start a Zap from a Pozy event

### Catch the hook

Create a Zap with the trigger **Webhooks by Zapier → Catch Raw Hook**. Catch Raw Hook keeps the body exactly as sent and includes the request headers, which the signature check needs. Copy the webhook URL Zapier shows.

### Point Pozy at it

Create a webhook endpoint in the dashboard, or with a request:

```bash Terminal
curl -X POST https://api.pozy.ai/v1/webhooks/settings \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Zapier: new leads",
    "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
    "events": ["lead.received"]
  }'
```

Copy `webhook.secret` from the response. Then send a test event with [`POST /v1/webhooks/test`](https://pozy.ai/docs/api/testWebhook) so Zapier has a sample to map.

### Verify the signature

Add **Code by Zapier → Run JavaScript**. In **Input Data**, map `rawBody` to the raw body from the trigger and `signature` to its `X-Pozy-Signature` header:

```js
const crypto = require("crypto");
const secret = "whsec_...";   // the endpoint secret

const expected = crypto.createHmac("sha256", secret).update(inputData.rawBody, "utf8").digest("hex");
if (expected !== (inputData.signature || "").trim()) {
  throw new Error("Signature does not match; ignoring this request.");
}

const event = JSON.parse(inputData.rawBody);
return { eventId: event.id, event: event.event, payload: JSON.stringify(event) };
```

Later steps can use `event` and fields from `payload`. Add a **Filter** on `event` if one Zap handles several event types.

Pozy expects a 2xx within 5 seconds; Zapier's catch hooks answer immediately. Pozy retries deliveries that fail, so the same event can trigger the Zap twice. If a duplicate would hurt, save each `eventId` with **Storage by Zapier** and stop the Zap when the id is already stored.

## Zap ideas

- **New blog post to social.** RSS by Zapier → Webhooks Custom Request with `customContent` per platform.
- **Approve before posting.** A form or a Slack button creates the post with `"requiresApproval": true`; a teammate approves it in the Pozy dashboard.
- **Leads to your CRM.** Catch Raw Hook on `lead.received`, verify, then create the contact in your CRM.
- **Failed post alerts.** Catch Raw Hook on `post.failed`, verify, then post to a Slack channel.

## When a step fails

| Response | Fix |
| --- | --- |
| `401` | Check the `Authorization` header: `Bearer`, a space, then the key |
| `400` | The JSON is broken or a field is wrong; read `error` and `param` in the response |
| `429` | Too many requests in one minute. Add **Delay by Zapier** in loops, or spread Zaps out |
| `5xx` | Turn on Zapier's autoreplay for the Zap. The idempotency key prevents a duplicate post |

Zapier counts a task for each action step, so a check that runs on every webhook adds up. Subscribe the endpoint only to the events the Zap needs.
