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.

Base URL
apiv7.sided.co
Auth
Two schemes
Format
application/json
Scope
clientId = network

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 · generate

Any 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.

0 · Mint your token — re-run whenever it expires
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
HeaderValue
x-access-tokenrequiredUser JWT, minted above. Issued against your service user.
x-network-domainrequiredA domain that resolves to your network. Without it the call is rejected even with a valid token.
Every write call
-H "x-access-token: <user JWT>" \
-H "x-network-domain: yournetwork.com" \
-H "Content-Type: application/json"
Where to find it
x-access-tokenmintedVia 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-domaindashboardYour network's Sided domain — dashboard, Settings → Network.
clientIddashboardShown 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 · results

Read calls are visitor-scoped. Mint a visitor token once per reader (or once per server session), then send it on reads. Three steps:

1 · Register a visitor uuid → 2 · exchange for a signed token
# 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}'
ParamDescription
embedDomainIdrequiredInteger id of your registered embed domain (not the domain string) — provided by your Sided contact when your domain is onboarded
clientIdrequiredNetwork id — see the Poll Feed Code on Settings → Integration (network="111")
3 · Send the token on every read
-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 unverified

Pass an article URL; Sided returns AI-generated polls from its content. count sets how many.

GET/api/admin/debates/generate-debates
ParamDescription
urlrequired*Article URL to generate from
clientIdrequiredNetwork the polls belong to
countoptionalNumber of polls
textoptionalRaw text instead of a URL — see 05
Request
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 authverified

Returns 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.

GET/api/embed/feed
ParamDescription
clientIdrequiredNetwork id
pageUrlrequiredPage the feed renders on
aiPollSourceUrlrequiredArticle source URL to filter by
embedPlacementIdrequiredPlacement id — Settings → Integration → Embed Placements → Copy Code (the id is in the snippet)
exclude / categories / categoryNames / uniqueUserId / isQuestionrequiredRequired by this endpoint today — pass empty strings / 0
Request
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>"
Response (verified)
{
  "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 authverified

Full poll by id — question, metadata, sides, and vote totals.

GET/api/debates/{debateId}
Request
curl "https://apiv7.sided.co/api/debates/446181" \
  -H "uuid-token: <uuidToken>"
Response (verified — top-level fields)
{
  "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 unverified

Plain-language summary you can render server-side and wrap in JSON-LD for citation. Two calls: read the poll (03), then summarize.

POST/api/poll-side-analytics/generate-summary
BodyDescription
contentrequiredText to summarize (max 10,000 chars)
pollDescriptionoptionalThe poll question, for context
Request
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?"}'
Response
{
  "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 unverified

Generate a poll from a text prompt, or post a fully specified poll.

GET/api/admin/debates/generate-debates
From a prompt
curl -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"
POST/api/debates/create
BodyDescription
thesisrequiredPoll question (1–140 chars)
sidesrequiredOptions, min 2 — each { "text": "…" }
startedAtrequiredStart date
formatTyperequired1–8; 1 = standard choice
clientIdoptionalNetwork id
Fully specified
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 unverified

Drive a sequence to a response target over a window, across a set of networks.

POST/api/campaigns
BodyDescription
sequenceIdrequiredSequence to run (see 07)
startDate / endDaterequiredCampaign window
responseCountTargetrequiredResponse quota
forNetworksrequiredNetworks to target, e.g. ["12","34"]
forGroupsrequiredGroups to target ([] if targeting by network)
title / descriptionrequiredName and summary
id / debateId / formIdrequiredPass 0 when unused — see note
Request
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 unverified

Chain multiple polls ending in a CTA: create the sequence, add each poll as a step, attach the CTA.

POST/api/sequences
BodyDescription
namerequiredSequence name
clientIdrequiredNetwork id
typerequired"Survey" or "Quiz"
questionOrderrequired"InOrder" or "Random"
idrequiredPass 0 to create
1 · Create the sequence
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
  }'
POST/api/sequence/steps/stepAndPartconfirm payload

Add 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.

POST/api/call-to-action
BodyDescription
titlerequiredCTA headline
buttonText / buttonLinkoptionalButton label and destination
idrequiredPass 0 to create
styleTitle / styleDescription / styleButtonrequiredStyle objects — {} for defaults
2 · Create the CTA, attach as the final step
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.