OAuth scopes
Survey Coder Connect uses granular scopes — coding:write rather than the legacy umbrella write. This lets partners ask for exactly the access they need (and lets end-customers see exactly what they’re granting on the consent screen).
Legacy scp_live_* keys still use the old read/write/import triad. The backend transparently maps both schemes — endpoints with requireScope('coding:write') accept either a Connect Bearer with that exact granular scope OR a legacy key with write.
Catalog
Section titled “Catalog”| Scope | Allows |
|---|---|
projects:read | List + read projects, questions, responses, and codebooks |
projects:write | Create / update / delete projects, questions, responses |
coding:write | Run coding jobs, post /v1/code, generate codebooks |
refinement:read | List refinement suggestions |
refinement:apply | Apply suggestions, bulk-apply, resolve, undo, export |
analytics:read | Cross-tab, segments, executive summaries, anomalies, all /v2/analytics/* |
connections:read | List platform connections (SurveyMonkey, Typeform, Qualtrics, etc.) |
connections:write | Create / test / delete platform connections |
webhooks:manage | Register, list, delete webhook endpoints |
connect:manage | Express-only. Create / list / delete managed sub-orgs. Not available to Standard flow. |
Requesting scopes
Section titled “Requesting scopes”In Standard flow, the partner specifies scopes in the consent URL:
https://surveycoder.io/oauth/authorize ?client_id=cc_talk2data &redirect_uri=https://app.talk2data.io/oauth/callback &scope=projects:read+coding:write+refinement:apply &state=<opaque> &response_type=codeThe end-customer sees these scopes in plain language on the consent screen. They can approve or deny but cannot pick a subset — it’s all-or-nothing per consent.
In Express flow, the partner specifies scopes in the client_credentials token request:
curl -X POST https://api.surveycoder.io/oauth/token \ -d grant_type=client_credentials \ -d client_id=cc_talk2data \ -d client_secret=cs_xxxx \ -d "scope=connect:manage coding:write refinement:apply"The partner can only request scopes that are in their oauth_clients.allowed_scopes list (set when Survey Coder registers the partner).
Legacy compatibility map
Section titled “Legacy compatibility map”When a request uses a legacy scp_live_* API key, the middleware maps the key’s old scope to a set of granular scopes:
read covers
Section titled “read covers”projects:readrefinement:readanalytics:readconnections:read
write covers
Section titled “write covers”projects:readprojects:writecoding:writerefinement:readrefinement:applyanalytics:readconnections:readconnections:writewebhooks:manage
import covers
Section titled “import covers”connections:readconnections:write
How it’s enforced
Section titled “How it’s enforced”Every endpoint calls requireScope('<granular>') middleware. The check works like this:
1. If req.oauth (Connect Bearer): pass when req.oauth.scopes.includes('<granular>')2. Else if req.apiKey (legacy): pass when one of req.apiKey.scopes covers '<granular>' via the map above3. Else: fail with 403 INSUFFICIENT_SCOPEThe INSUFFICIENT_SCOPE error envelope includes the required scope so you can diagnose:
{ "success": false, "error": { "code": "INSUFFICIENT_SCOPE", "message": "Required scope: refinement:apply", "doc_url": "https://docs.surveycoder.io/errors/insufficient-scope/", "request_id": "req_..." }}Picking the right scopes for your integration
Section titled “Picking the right scopes for your integration”Common combinations:
- “Read-only analytics dashboard”:
projects:read,analytics:read. - “Code on behalf of customer”:
projects:read,coding:write,refinement:read. - “Code + auto-apply refinement”:
projects:read,coding:write,refinement:read,refinement:apply. - “Full workflow including imports”:
projects:read,projects:write,coding:write,refinement:read,refinement:apply,connections:read,connections:write. - Express partner: add
connect:manageto whatever subset above.
Ask only for what you need — the consent screen is more compelling when the scope list is shorter.