Перейти к содержимому

API quickstart

The full contract is openapi.json (OpenAPI 3.1). This page gets you from zero to a verified webhook in about ten minutes, using only curl. Policy details (rate limits, errors, idempotency, delivery guarantees, versioning) live in policies.md.

Base URL: https://api.feedigy.com/v1. Every request needs a bearer token. Every response carries a request_id for support tracing — in the error body on failures, in the X-Request-Id header on successes. Keep it; support can trace any call from it.

1. Create a token

In the app, open Settings → API (/settings?tab=api), give the token a name, tick the abilities it needs (feeds:read, posts:write, …), and copy the bearer. It is shown once — only the last four characters are stored for the Settings list. With nothing ticked the token gets the read abilities explicitly; there is no wildcard.

Terminal window
export API=https://api.feedigy.com/v1
export TOKEN=your-token-here

2. Check the account

Terminal window
curl -s $API/me -H "Authorization: Bearer $TOKEN"
{ "data": { "id": 7, "email": "you@example.com", "plan": "pro",
"limits": { "api_requests_per_minute": 180 } } }

limits.api_requests_per_minute always matches the X-RateLimit-Limit response header (unlimited plans report the 3000 protective ceiling, not null). A plan without API access answers 403 here, not 429.

3. List feeds

Terminal window
curl -s "$API/feeds?status=active&per_page=5" -H "Authorization: Bearer $TOKEN"
{ "data": [ … ],
"meta": { "per_page": 5, "next_cursor": "eyJ…", "has_more": true } }

Pagination is cursor-based: pass meta.next_cursor as ?cursor= for the next page. per_page defaults to 25 and caps at 100. Unknown query keys are a 422, never silently ignored — if you mistype a filter you hear about it.

4. Create a post (idempotent)

Writes require Content-Type: application/json and, on POST/PATCH, an Idempotency-Key header (1–255 printable ASCII characters, kept 24 h). Replaying a key returns the original response without writing again; automation platforms retry aggressively, so generate one key per logical operation (a UUID works) and reuse it across retries:

Terminal window
export KEY=$(php -r 'echo Ramsey\Uuid\Uuid::uuid4()->toString();' 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')
curl -s -X POST $API/posts \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $KEY" \
-d '{"feed_id": 12, "title": "Hello, API", "status": "draft"}' -D -

A 201 answers with the post and a Location header. Send the exact same request again and you get the same 201 with the same body — one post, not two. Send a different body under the same key and you get a 422: a fresh logical operation needs a fresh key.

status is draft, scheduled (with a scheduled_at timestamp carrying an explicit offset), or published. Unknown body fields are a 422.

5. Receive a post.published webhook

In Settings → API, add an endpoint: your public HTTPS URL subscribed to post.published, and copy the one-time secret. Publish the draft from step 4 (in the app, or PATCH /v1/posts/{id} with {"status":"published"}) and your receiver gets:

POST /your-path Content-Type: application/json
X-Feedigy-Event: post.published
X-Feedigy-Delivery: <per-endpoint delivery id, an integer>
X-Feedigy-Timestamp: <unix seconds>
X-Feedigy-Signature: t=<ts>,v1=<hex HMAC-SHA256>
{ "event": "post.published", "event_id": "<publication episode UUID>",
"version": 1, "occurred_at": "2026-10-09T…Z",
"data": { "post": { "id": 99, "title": "Hello, API", … },
"feed": { "id": 12, "title": "…", … } } }

Verify before parsing: HMAC-SHA256 over <timestamp>.<exact raw body> with your secret, lowercase hex, constant-time compare; reject timestamps outside a 5-minute window; store X-Feedigy-Delivery ids — delivery is at-least-once, so a retry repeats the id with the same body. During the 24 h after a secret rotation both the current and the previous signature are sent (t=<ts>,v1=<current>,v1=<previous>); accept either.

$raw = file_get_contents('php://input');
$timestamp = null;
$signatures = [];
foreach (explode(',', $_SERVER['HTTP_X_FEEDIGY_SIGNATURE'] ?? '') as $part) {
[$k, $v] = array_pad(explode('=', $part, 2), 2, '');
if ($k === 't') {
$timestamp = $v;
} elseif ($k === 'v1' && $v !== '') {
$signatures[] = $v; // after a rotation there are two; accept either
}
}
$okAge = $timestamp !== null && abs(time() - (int) $timestamp) <= 300;
$expected = hash_hmac('sha256', ($timestamp ?? '').'.'.$raw, $secret);
$okSig = false;
foreach ($signatures as $sig) {
if (strlen($sig) === strlen($expected) && hash_equals($expected, $sig)) {
$okSig = true;
}
}
if (! $okAge || ! $okSig) {
http_response_code(401);
exit;
}
// …then dedupe on X-Feedigy-Delivery and process $raw…

(Accept any v1= value in the header — after a rotation there are two. The reference consumer in deploy/webhook-reference-consumer shows the same check in Node.)

The other two events work identically: publisher.run.failed (a scheduled auto-publisher run failed terminally) and channel.token_expired (a connected account needs reconnecting). Payload shapes are WebhookPostPublished, WebhookPublisherRunFailed and WebhookChannelTokenExpired in openapi.json.

Next steps

  • API Policies — rate limits and tiers, the error envelope, idempotency semantics, delivery guarantees, versioning.
  • OpenAPI spec — the machine-readable contract.
  • API Reference — every endpoint, field and webhook event.

Keep posting through busy weeks

Set sources, keywords and posting hours once. Feedigy posts to your channels while you work on other things.

No card required. The Free plan has no time limit.