Partner API · v7
Build with Sided polls.
Generate polls from your articles, read live results, and run response campaigns across one or many networks in your group — over REST.
Read first
Business errors return HTTP 200 with status: 0 and an errorCode; auth failures return HTTP 4xx with {"message":"Unauthorized"} — handle both. A poll is a debate, a network is a client. Target one network with clientId, several with clientIds=1,2,3 / forNetworks:[…], or a whole groupId. Keep all tokens server-side.
A
Write auth
create · generateAny call that creates or generates (polls, sequences, CTAs, summaries) needs a user token plus a network header. Mint the token from your service user's credentials, then send two headers on every call. Your Sided contact provisions the service user.
curl -X POST "https://api2.sided.co/auth/signin" \
-H "Content-Type: application/json" \
-d '{"field":"<service user email>","password":"<service user password>"}'
# → { "status": "success", "token": "eyJhbGci…" } ← use as x-access-token below
# note the different host: sign-in lives on api2.sided.co, everything else on apiv7.sided.co| Header | Value | |
|---|---|---|
| x-access-token | required | User JWT, minted above. Issued against your service user. |
| x-network-domain | required | A domain that resolves to your network. Without it the call is rejected even with a valid token. |
-H "x-access-token: <user JWT>" \
-H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json"| Where to find it | ||
|---|---|---|
| x-access-token | minted | Via the sign-in call above, with the service user your Sided contact provisions. Heads up: the API KEY on your dashboard's Integration page is the embed key — it is not this token, and this API rejects it (Unauthorized). Same for the SSO Access Token on that page. |
| x-network-domain | dashboard | Your network's Sided domain — dashboard, Settings → Network. |
| clientId | dashboard | Shown in the Poll Feed Code on Settings → Integration: network="111" means your clientId is 111. |
Auth here is not Authorization: Bearer. The token expires — when a call returns HTTP 4xx {"message":"Unauthorized"}, re-run the sign-in and retry. Keep the credentials and token server-side only, never in browser code.
B
Read auth
feed · resultsRead calls are visitor-scoped. Mint a visitor token once per reader (or once per server session), then send it on reads. Three steps:
# 1
curl -X POST "https://apiv7.sided.co/api/master/uuid/issue" \
-H "Content-Type: application/json" \
-d '{"embedDomainId": 501, "clientId": 111}'
# 2 → { "data": { "uuidToken": "eyJhbGci…" } }
curl -X POST "https://apiv7.sided.co/api/master/uuid/xfer-token" \
-H "Content-Type: application/json" \
-d '{"embedDomainId": 501, "clientId": 111}'| Param | Description | |
|---|---|---|
| embedDomainId | required | Integer id of your registered embed domain (not the domain string) — provided by your Sided contact when your domain is onboarded |
| clientId | required | Network id — see the Poll Feed Code on Settings → Integration (network="111") |
-H "uuid-token: <uuidToken from step 2>"
# or as a query param: ?clientUUIDToken=<uuidToken>The token is a signed JWT; the server derives the visitor from it. Treat it as required on all reads: GET /api/debates/{id} rejects calls without it (errorCode 6); the feed degrades or errors without it.
01
Generate polls from a URL
write authshape unverifiedPass an article URL; Sided returns AI-generated polls from its content. count sets how many.
/api/admin/debates/generate-debates| Param | Description | |
|---|---|---|
| url | required* | Article URL to generate from |
| clientId | required | Network the polls belong to |
| count | optional | Number of polls |
| text | optional | Raw text instead of a URL — see 05 |
curl -G "https://apiv7.sided.co/api/admin/debates/generate-debates" \
--data-urlencode "url=https://news.example.com/story" \
--data-urlencode "clientId=12" --data-urlencode "count=3" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com"*Provide url or text. Returns the generated polls, persisted to the network.
02
Fetch polls for a URL
read authverifiedReturns a manifest of polls attached to an article. aiPollSourceUrl filters to polls from that source page. This gives ids + type — fetch each poll's detail via 03.
/api/embed/feed| Param | Description | |
|---|---|---|
| clientId | required | Network id |
| pageUrl | required | Page the feed renders on |
| aiPollSourceUrl | required | Article source URL to filter by |
| embedPlacementId | required | Placement id — Settings → Integration → Embed Placements → Copy Code (the id is in the snippet) |
| exclude / categories / categoryNames / uniqueUserId / isQuestion | required | Required by this endpoint today — pass empty strings / 0 |
curl -G "https://apiv7.sided.co/api/embed/feed" \
--data-urlencode "clientId=111" \
--data-urlencode "aiPollSourceUrl=https://news.example.com/story" \
--data-urlencode "pageUrl=https://news.example.com/story" \
--data-urlencode "embedPlacementId=340" \
--data-urlencode "exclude=" --data-urlencode "categories=" \
--data-urlencode "categoryNames=" --data-urlencode "uniqueUserId=0" \
--data-urlencode "isQuestion=0" \
-H "uuid-token: <uuidToken>"{
"status": 1,
"data": [
{ "contentType": "Poll", "contentTypeId": 446345,
"campaignId": null, "campaignName": null,
"askId": null, "askName": null, "isSponsored": 0 },
{ "contentType": "Poll", "contentTypeId": 446300, /* … */ }
]
}contentTypeId is the debateId — pass it to 03 for the question, sides, and tallies.
AltBy placement: GET /api/feeds/getDebateByEmbedPlacement?clientId=&embedPlacementId=&pageUrl=
03
Read results for a poll
read authverifiedFull poll by id — question, metadata, sides, and vote totals.
/api/debates/{debateId}curl "https://apiv7.sided.co/api/debates/446181" \
-H "uuid-token: <uuidToken>"{
"status": 1,
"data": {
"id": 446181,
"thesis": "Where do you do most of your grocery shopping?",
"slug": "where-do-you-do-most-of-your-grocery-shopping",
"clientId": 111,
"votes": 327,
"arguments": 1,
"comments": 0,
"articleSourceId": 121310,
"startedAt": "2026-07-01T06:28:40.000Z",
"endedAt": "2026-07-08T06:27:38.000Z",
"isQuestion": 0, "isDraft": 0, "requireEmail": -1
// + sides[] with per-option vote counts
}
}votes is the total across sides. The sides[] array (each option + its count) is in the full payload.
04
Summarize results for an article
write authshape unverifiedPlain-language summary you can render server-side and wrap in JSON-LD for citation. Two calls: read the poll (03), then summarize.
/api/poll-side-analytics/generate-summary| Body | Description | |
|---|---|---|
| content | required | Text to summarize (max 10,000 chars) |
| pollDescription | optional | The poll question, for context |
curl -X POST "https://apiv7.sided.co/api/poll-side-analytics/generate-summary" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json" \
-d '{"content":"62% chose supermarkets, 24% discount grocers, 14% online","pollDescription":"Where do you do most of your grocery shopping?"}'{
"status": "success",
"data": {
"topic": "grocery shopping habits",
"sentiment": "neutral",
"summary": "Most readers shop at supermarkets, with discount grocers second"
}
}Pattern: pull tallies from 03, feed them here, emit the summary as text + JSON-LD on your page. Structured-data markup is on your side.
AltPer-side summaries for a whole poll: POST /api/master/side-advertiser-summaries/{debateId}
05
Create a poll from a prompt
write authshape unverifiedGenerate a poll from a text prompt, or post a fully specified poll.
/api/admin/debates/generate-debatescurl -G "https://apiv7.sided.co/api/admin/debates/generate-debates" \
--data-urlencode "text=Ask readers whether the city should expand bike lanes" \
--data-urlencode "clientId=12" --data-urlencode "count=1" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com"/api/debates/create| Body | Description | |
|---|---|---|
| thesis | required | Poll question (1–140 chars) |
| sides | required | Options, min 2 — each { "text": "…" } |
| startedAt | required | Start date |
| formatType | required | 1–8; 1 = standard choice |
| clientId | optional | Network id |
curl -X POST "https://apiv7.sided.co/api/debates/create" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json" \
-d '{
"thesis": "Should the city expand bike lanes?",
"sides": [{"text": "Yes, expand them"}, {"text": "No, leave as is"}],
"startedAt": "2026-07-02", "formatType": 1, "clientId": 12
}'The network's subscription must be active. Poll creation is subject to a daily cap.
06
Run a campaign
write authshape unverifiedDrive a sequence to a response target over a window, across a set of networks.
/api/campaigns| Body | Description | |
|---|---|---|
| sequenceId | required | Sequence to run (see 07) |
| startDate / endDate | required | Campaign window |
| responseCountTarget | required | Response quota |
| forNetworks | required | Networks to target, e.g. ["12","34"] |
| forGroups | required | Groups to target ([] if targeting by network) |
| title / description | required | Name and summary |
| id / debateId / formId | required | Pass 0 when unused — see note |
curl -X POST "https://apiv7.sided.co/api/campaigns" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json" \
-d '{
"id": 0, "title": "Bike lane pulse — July", "description": "Reader sentiment",
"sequenceId": 812, "debateId": 0, "formId": 0,
"responseCountTarget": 2000, "responseCount": 0,
"startDate": "2026-07-05", "endDate": "2026-07-19",
"runFor": "network", "forNetworks": ["12","34"], "forGroups": [],
"slotNumber": 2, "isActive": 1, "isSponsored": 0
}'Heads up: requires id, debateId, formId even when running a sequence — pass 0. forNetworks is how one campaign fans out across many networks.
AltA newer POST /api/v7/campaigns namespace exists (with GET /api/v7/campaigns/{id}/stats) but its body isn't finalized — ask before you build against it.
07
Build a sequence
write authshape unverifiedChain multiple polls ending in a CTA: create the sequence, add each poll as a step, attach the CTA.
/api/sequences| Body | Description | |
|---|---|---|
| name | required | Sequence name |
| clientId | required | Network id |
| type | required | "Survey" or "Quiz" |
| questionOrder | required | "InOrder" or "Random" |
| id | required | Pass 0 to create |
curl -X POST "https://apiv7.sided.co/api/sequences" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json" \
-d '{
"id": 0, "name": "Transit priorities", "clientId": 12,
"type": "Survey", "questionOrder": "InOrder",
"showCorrectAnswers": "Immediately", "questionTimer": 0,
"numberOfQuestions": 3, "startedAt": "2026-07-05", "endedAt": "2026-07-19",
"isDeployed": false, "deployedAt": "", "deployedBy": 0
}'/api/sequence/steps/stepAndPartconfirm payloadAdd one step per poll, referencing the poll id. Exact body isn't finalized — inspect GET /api/sequences/{id} on an existing sequence for the shape. Repeat per poll.
/api/call-to-action| Body | Description | |
|---|---|---|
| title | required | CTA headline |
| buttonText / buttonLink | optional | Button label and destination |
| id | required | Pass 0 to create |
| styleTitle / styleDescription / styleButton | required | Style objects — {} for defaults |
curl -X POST "https://apiv7.sided.co/api/call-to-action" \
-H "x-access-token: <user JWT>" -H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json" \
-d '{
"id": 0, "title": "See the full results",
"buttonText": "Read more", "buttonLink": "https://news.example.com/results",
"linkToNextStep": 0, "styleTitle": {}, "styleDescription": {}, "styleButton": {}
}'AltGenerate a whole sequence with AI: POST /api/sequences/ai-sequence-gennerator
verified = confirmed against the live API · shape unverified = being finalized. Credentials and ids come from your Sided contact — book a call to get set up.