Skip to main content
Background work continues after the request returns; poll the endpoint in the table below or receive a webhook. Polling needs only your API key. Webhooks need an HTTPS endpoint that support registers for your organization (Set up a webhook).

What runs in the background

Six operations start work that outlives the response; the table says what each request answers, where to poll, and which states are final.
Every endpoint under /content_editors/{content_editor_id}/ answers 409 with reason conflict and the message Content editor must be in completed state until the editor is completed; that includes creating an AI Article. Errors documents the envelope.

Content Editor states

A failed editor has no retry call; read error.reason and create a new one.

AI Article states

An article pauses in waiting_for_user_input only when you created it with manual_outline: true.

Auto-Optimize states

A completed job also carries a result, so check it before you read the content again.

Recognizing a fresh score

A score is fresh when status is ready and calculated_at is later than the value you read before the update.
Give a polling loop a deadline: when the SEO recalculation fails, Surfer sends content_editor.seo_score.failed but status stays calculating. Troubleshooting covers the recovery.
After Update Content Editor content returns 204, the SEO status flips to calculating at once. A ready AI Search score flips too, unless the editor’s AI Search guidelines have not finished generating; then that score stays as it was and only the SEO score recalculates. calculated_at is null until the first calculation completes.

Combined Content Score

The editor’s content_score object on Get Content Editor carries seo, ai_search, and total. total is the rounded average of the two, capped at 100, when both exist, and equals seo when the AI Search score is missing.

Set up a webhook

One HTTPS URL per organization receives every event; support registers it and gives you the verification key.
1

Serve an HTTPS endpoint

Serve an HTTPS URL that accepts POST requests with a JSON body.
2

Answer with a 2xx status within 30 seconds

Queue the body and answer before you process it. An answer after 30 seconds counts as a failed delivery (Delivery).
3

Ask support to register the URL

Send the URL through the same support channel as your API key (Authentication). Support registers it for your organization and replies with the verification key.
4

Store the verification key

Keep it where your endpoint can read it, for example in an environment variable.
5

Reject deliveries with a different key

Compare the verification-key header of each request with the stored key, and drop bodies whose key differs.
6

Create a test Content Editor

This spends one Content Editor credit (Rate limits and credits). Call Create Content Editor with any keyword.
7

Wait for the first event

content_editor.initialization.completed arrives when initialization finishes and carries the editor’s id as draft_id.
To change the URL, ask support again. The new URL replaces the old one and comes with a new verification key, so update both in your endpoint together.

Delivery

Each event is one POST with a JSON body and the verification key in a header. Your response status decides whether Surfer marks the delivery done, retries it, or stops. A delivery looks like this, here for content_editor.initialization.completed:
Deliveries can arrive out of order: a retried event lands after events that came later. If your endpoint processed a delivery but answered late, the retry brings the same id again. A failed SEO recalculation runs a second time, so one content update can send content_editor.seo_score.failed twice, or once before content_editor.seo_score.calculated. Keep the polling endpoints above as the way to re-sync after a gap.

Which work sends events

Most events are sent only for work started through the API; work started in the Surfer app sends none, with one exception.

Event catalog

Every event names the Content Editor: draft_id is the editor’s id, and permalink_hash is the hash of its edit link (List Content Editor permalinks). The AI Article events carry no article id. Use draft_id to list the editor’s articles and read the newest one.

Which endpoint triggers which events

Each operation sends its own events first; when the document changed, the score events follow.