Docs

Keep your app in sync

Mirror posts and their status in your own database with webhooks and a periodic catch-up.

If your app shows posts and their status, keep a copy in your database. Webhooks tell you about changes as they happen; a regular catch-up call fills any gap, for example while your server was down.

1. Subscribe to post events

curl -X POST https://feedhorizon.com/api/v1/webhooks \
  -H "Authorization: Bearer $FEEDHORIZON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sync",
    "url": "https://api.example.com/webhooks/feedhorizon",
    "events": ["post.scheduled", "post.updated", "post.deleted", "post.target.published", "post.target.failed", "post.published", "post.partial", "post.failed"]
  }'

Handle each event idempotently: the same event can arrive twice (it carries the same id then), and events can arrive out of order. The simplest robust handler loads the post fresh instead of applying the event:

async function onEvent({ event, data }) {
    if (event === 'post.deleted') return db.posts.delete(data.postId);
    const res = await fetch(`https://feedhorizon.com/api/v1/posts/${data.postId}`, {
        headers: { Authorization: `Bearer ${process.env.FEEDHORIZON_API_KEY}` },
    });
    if (res.status === 404) return db.posts.delete(data.postId);
    const { data: post } = await res.json();
    await db.posts.upsert(post.id, post);
}

2. Catch up regularly

Every few minutes, ask for the posts that changed since your last sync. Store the time before each call and pass it next time:

curl "https://feedhorizon.com/api/v1/posts?updatedSince=2026-11-10T08:00:00Z" \
  -H "Authorization: Bearer $FEEDHORIZON_API_KEY"

Deleted posts do not show up in this list; only the post.deleted webhook tells you about them.

3. A calendar view

For a calendar, GET /posts?from=…&to=… returns the posts whose publish time (published, else scheduled) falls in that range, oldest first. Filter by profileId and status as needed. See List posts.