> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ocoya.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom destinations

> Receive scheduled posts at your own endpoint, and what your endpoint has to reply.

A **custom destination** is a channel that points at a URL you own rather than at a social network. Ocoya `POST`s each scheduled post to it as JSON, and your code decides what publishing means — an article in your CMS, a row in a database, a message on a queue.

This page is the contract your endpoint has to satisfy. For the full request body, see [Custom (webhook)](/help/channels/custom-webhook) in the help center.

<Note>
  Custom destinations are connected in the Ocoya app — **Channels → Connect channel → Custom**. There is no REST or MCP endpoint that connects one, so this page describes the endpoint *you* write, not one you call.
</Note>

## What Ocoya sends

A `POST` with a JSON body and `Content-Type: application/json`, plus any headers you configured on the channel.

```json theme={null}
{
  "event": "post.published",
  "post": {
    "id": "clx…",
    "caption": "…",
    "options": { "title": "…", "slug": "…" }
  }
}
```

## What your endpoint must reply

Three things, all required. A publish missing any of them is recorded as failed.

### 1. A 2xx status

Anything else is recorded as a failed publish, and the status and response body are shown on the post so you can see what your endpoint said.

### 2. An `id`

Your own identifier for what you just created — a database row id, a CMS document id, whatever you would look the record up by. Ocoya stores it and sends it back to your delete URL, so **this is what you delete by**.

### 3. A `url`

The address you published to. It becomes the post's **View post** link in Ocoya.

```json theme={null}
{
  "id": "9f2c1a",
  "url": "https://yourblog.com/post"
}
```

| Field | Type   | Required | What it does                                                            |
| ----- | ------ | -------- | ----------------------------------------------------------------------- |
| `id`  | string | Yes      | Your identifier for the created content. Sent back as `id` on a delete. |
| `url` | string | Yes      | The published address. Becomes the post's **View post** link.           |

<Warning>
  These are the only two keys read from the reply, and both spellings are exact. `permalink` was accepted as a synonym for `url` in earlier versions and no longer is.
</Warning>

<Note>
  Both are read from **this reply only**. There is no callback to send them in afterwards, so an endpoint that queues the work has to know its id and address before it answers rather than after the work finishes.
</Note>

If your endpoint answers `2xx` without them, Ocoya records the publish as failed and says so on the post. The content may well exist at your destination at that point — the message says as much, because republishing blindly would create a second copy.

## Timing

Your endpoint has **30 seconds** to respond. Acknowledge the request first and do the slow work afterwards, rather than the other way round.

A redirect is followed rather than treated as a failure, as long as the address it points at passes the same checks the original did. The method is kept, so your endpoint still receives a `POST` with the body. Three hops is the limit.

## Deleting

If the channel has a delete URL, deleting a post in Ocoya sends a `post.deleted` request there carrying the `id` you returned when it published:

```json theme={null}
{
  "event": "post.deleted",
  "id": "9f2c1a",
  "post": { … }
}
```

`id` is at the top level and on its own — the identifier you issued, with nothing to parse and no wrapper to reach through. The `url` you returned is **not** sent back: an endpoint deleting by its own id has no use for an address it already knows.

Answer any **2xx**. If the post is already gone on your side, `404` or `410` is recorded as already deleted rather than as a failure. The post is deleted in Ocoya either way.

## Related

<Card title="Custom (webhook)" icon="webhook" href="/help/channels/custom-webhook">
  The full payload, headers, limits and common problems.
</Card>
