Getting started
QuickstartConceptsRecommended setupsAuthenticationAPIs
Delivery APIOutbound webhooksLogsIngest webhookImagesErrorsLibraries
TypeScript SDKReference
Plans and limitsIngest webhook
A partner posts articles to a url. They arrive as articles.
Create a source
Create a source in the console. Give it a name. Tick the sites the articles land on.
Copper CMS issues a url. Hand it over unchanged.
POST https://api.coppercms.com/v1/hook/8f2c…c1a9.k3Rd… content-type: application/json { "id": "post-1041", "title": "A post from the partner", "body": "<p>The body of the post.</p>", "slug": "a-post-from-the-partner", "excerpt": "One line of summary.", "author": "Jane Doe", "tags": ["news"], "publishedAt": "2026-01-09T09:00:00Z" }
There is nothing to sign. There is no key to send. The url is the credential.
Field names
A new source expects these names.
| Send | Becomes | Required |
|---|---|---|
| title | The headline | Yes |
| body | The article as HTML | No |
| id | The id the partner already uses | No |
| slug | The url path of the post | No |
| excerpt | The summary | No |
| author | The byline | No |
| tags | Tags | No |
| publishedAt | The publication time | No |
Only the title is required. A payload that produces no title answers 400.
A slug is used exactly as sent, so an article keeps the url it already has. One carrying a slash, a space or a capital is not a path we can serve, and those fall back to the title. A slug already taken on that site takes a numbered suffix.
Sending a different slug for an article that is not live yet moves it. Once the article is live the url stays where it is, and the slug that was sent is recorded on the pending revision for a person to decide on — a live url is one readers and search engines already hold, and the redirect for a moved one belongs to the site serving it.
publishedAt may be any date, including one in the past — an archive keeps the dates it already has. A later send of the same article corrects it, up until the article is published.
Different field names
A partner can keep the names already in use.
Ask the partner to send an article. The source page shows the fields that arrived. It guesses which field is which. Any of it can be corrected.
Save the mapping. The next article the partner sends lands under it.
The article used for this is a sample. It does not become an article.
Nested fields work. A path like media.thumbnail.url reads that field. A path like categories[*].name collects every name in a list.
Cover image
A cover is sent beside the article and not inside the body, as coverImage.url. A partner that sends it under another name maps it on the source page.
The cover and every image in the body are copied to Copper CMS when the article arrives. See Images.
The response
200 OK { "data": { "id": "7d2a…", "created": true, "unrouted": false, "pendingRevision": false } }
| Field | Meaning |
|---|---|
| id | The article in Copper CMS. |
| created | True for a new article. False for one that already existed. |
| unrouted | True if the article arrived with nowhere to go. It is held. |
| pendingRevision | True if the article was already live and the change waits for review. |
A new article answers 200. A re-send answers 200. A partner never has to read the status code to know the article arrived.
Sending the same article again
An article is matched on the id the partner sends. Sending it again updates the article. It does not make a second copy.
An article that is already live is updated through review. The send is held as a pending revision and the live article is untouched until somebody approves it in the dashboard. Approving replaces the article with the send: the excerpt, the body, the cover, the author, the tags and the date. A field the send leaves out is cleared, so a send with no cover removes the cover. A send with no title, body or date keeps the one the article has. The article stays published.
A slug sent with it is held the same way. Approving a revision whose slug differs unpublishes the article, renames it and publishes it again, so a subscriber is told the url moved.
Only the newest send is reviewed. Approving it retires the ones before it.
A source whose updated article routing is set to approve and publish automatically skips the review. The send is applied as it arrives, slug included.
Sending a deleted article again restores it.
Deleting an article
A partner deletes an article it sent with the id from the response.
DELETE https://api.coppercms.com/v1/hook/8f2c…c1a9.k3Rd…/7d2a…
200 OK { "data": { "id": "7d2a…" } }
The article goes to the trash. It can be restored in the console for 30 days. A live article comes off every site it was on, and subscribers are sent post.unpublished.
A source can delete only the articles it sent. Any other id answers 404, and so does an article already in the trash.
Routing
Articles land on the sites set on the source. A payload can name sites instead.
An article that cannot be routed anywhere goes to Unrouted in the console. It can be sent to a site by hand.
Size
| Largest request body | 1 MB |
|---|
A larger body answers 413. The body is dropped once the limit is passed.
When an article is refused
A refused article is kept. The most recent failure for each source is held. The fields that arrived can be looked at and the mapping fixed.
See Errors.
