Skip to content

REST API

Askalot provides a REST API for programmatic access to all platform capabilities. The API enables you to automate survey workflows, integrate with external systems, and build custom applications on top of the Askalot platform.

Getting Started

Base URL

https://portor.<tenant>.askalot.io/api/v1

Replace <tenant> with your organization's tenant name (e.g., portor.acme.askalot.io).

Authentication

All API requests require authentication using a personal API token.

Generating a Token:

  1. Log in to https://roundtable.<tenant>.askalot.io
  2. Navigate to Profile Settings
  3. Scroll to "API Tokens" section
  4. Click "Generate New Token"
  5. Copy the token (shown only once)

Keep Your Token Secure

API tokens provide full access to your account. Never share tokens or commit them to version control.

Using Your Token:

Include your token in the X-Api-Token header:

curl -H "X-Api-Token: your_token_here" \
  https://portor.<tenant>.askalot.io/api/v1/projects

Token Properties:

  • Format: tokens are aslat_-prefixed (aslat_<32 characters>); the prefix lets secret scanners flag an accidental commit. Only the prefix is stored server-side for identification — the full token is shown once at creation and never again.
  • Header: send it in the X-Api-Token header. This is the REST credential path; the Authorization: Bearer header on Portor is reserved for OAuth access tokens (the MCP transport), not API tokens.
  • Scope: authenticates the REST API only. It inherits your account's roles and permissions and is not accepted by the MCP interface.
  • Revocation: revoke at any time from Profile Settings; revocation takes effect immediately.

Token Lifetime & Rotation

You choose an expiry when creating a token — the Profile Settings dropdown offers 30 days, 90 days (the default), or 1 year. An expiry is required; there is no "never-expiring" option for new tokens (only older, grandfathered tokens may lack one).

  • Profile Settings shows each token's expiry date and days remaining, and flags a token for rotation once it is within 14 days of expiring.
  • To rotate: create a replacement token, update your integration to use it, then revoke the old one. Tokens do not auto-renew — an expired token returns 401 and must be replaced.

REST vs MCP authentication

This API token is for REST clients (integrations, CI, server-to-server, and sandboxed code that drives the REST API for bulk work). The MCP interface does not accept it — MCP authenticates through an interactive OAuth 2.1 browser flow and resolves OAuth (or synthetic service) bearer tokens exclusively. Use the API token for non-interactive REST; use the OAuth sign-in for MCP clients like the Askalot plugin.

Interactive Documentation

Explore the API interactively using Swagger UI at:

https://portor.<tenant>.askalot.io/api/v1/docs

The interactive documentation lets you:

  • Browse all available endpoints
  • View request/response schemas
  • Test API calls directly in your browser

Core Resources

Project Ownership

Projects, Bundles, Questionnaires, Campaigns, Respondents, Surveys, Respondent Pools, and Sampling Strategies are scoped to the Project that owns them: creating, updating, or deleting one of these resources requires you to be an owner of its parent Project (or an organization administrator). See Entity Isolation & Sharing for the full ownership model.

Projects

Projects are top-level containers for organizing your survey research.

Method Endpoint Description
GET /api/v1/projects List all projects
POST /api/v1/projects Create a new project
GET /api/v1/projects/{id} Get project by ID
PUT /api/v1/projects/{id} Update a project
DELETE /api/v1/projects/{id} Delete a project
POST /api/v1/projects/{id}/owners Add project owners
DELETE /api/v1/projects/{id}/owners Remove project owners

Owners stay within the organization

Owner targets must belong to the project's organization. A request naming a user from another organization is rejected with 403 ({"error": "forbidden"}) and no partial write occurs — either every listed user is applied or none is.

Example: Create a Project

curl -X POST https://portor.<tenant>.askalot.io/api/v1/projects \
  -H "X-Api-Token: your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Customer Satisfaction Study",
    "description": "Q1 2026 customer feedback survey"
  }'

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Customer Satisfaction Study",
  "description": "Q1 2026 customer feedback survey",
  "created_at": "2026-01-02T10:30:00Z",
  "questionnaire_ids": [],
  "campaign_ids": []
}

Questionnaires

Questionnaires define survey structure using QML (Questionnaire Markup Language).

Method Endpoint Description
GET /api/v1/questionnaires List all questionnaires
POST /api/v1/questionnaires Create a new questionnaire
GET /api/v1/questionnaires/{id} Get questionnaire by ID
PUT /api/v1/questionnaires/{id} Update a questionnaire
DELETE /api/v1/questionnaires/{id} Delete a questionnaire

Query Parameters:

  • project_id - Filter by project
  • status - Filter by status (draft, active, paused, completed)

Campaigns

Campaigns link questionnaires to respondents for data collection.

Method Endpoint Description
GET /api/v1/campaigns List all campaigns
POST /api/v1/campaigns Create a new campaign
GET /api/v1/campaigns/{id} Get campaign by ID
PUT /api/v1/campaigns/{id} Update a campaign
DELETE /api/v1/campaigns/{id} Delete a campaign
PUT /api/v1/campaigns/{id}/pool Assign a respondent pool to the campaign
GET /api/v1/campaigns/{id}/pool Get the campaign's assigned pool
PUT /api/v1/campaigns/{id}/questionnaire Change the campaign's questionnaire
POST /api/v1/campaigns/{id}/interviewers Add interviewers to the campaign
DELETE /api/v1/campaigns/{id}/interviewers Remove interviewers from the campaign

Query Parameters:

  • project_id - Filter by project
  • questionnaire_id - Filter by questionnaire
  • status - Filter by status
  • expand=true - Include related objects (project, questionnaire, respondents)

Campaign Modes:

Askalot supports two campaign modes:

  • Direct Mode: Respondents complete surveys independently via unique links
  • Interviewer-Assisted Mode: Interviewers administer surveys to respondents by phone or in person

The mode is determined automatically based on whether interviewers are assigned to the campaign.

Respondents

Respondents are the survey targets—the people being surveyed.

Method Endpoint Description
GET /api/v1/respondents List all respondents
POST /api/v1/respondents Create a new respondent
GET /api/v1/respondents/{id} Get respondent by ID
PUT /api/v1/respondents/{id} Update a respondent
DELETE /api/v1/respondents/{id} Delete a respondent

Query Parameters:

  • source_system - Filter by source (e.g., crm, panel_provider)
  • campaign_id - Filter by campaign

Note: Interviewers are Users with the interviewer role. They are managed through the /api/v1/users endpoint.

Example: Import a Respondent

curl -X POST https://portor.<tenant>.askalot.io/api/v1/respondents \
  -H "X-Api-Token: your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "email": "[email protected]",
    "age": 35,
    "gender": "female",
    "location": "New York",
    "external_id": "CRM-12345",
    "source_system": "salesforce",
    "custom_attributes": {"state": "NY", "zip_code": "10001"}
  }'

Demographics Fields:

Respondents support demographic data for targeting and analysis:

  • age - Respondent age
  • gender - Gender identity
  • location - Geographic location (free text, typically "City, Country")
  • external_id - Your system's identifier
  • source_system - Source of the respondent data

Geography finer than location — street, city, state, postal code, country as separate values — goes in custom_attributes, and is addressable as custom_attributes.<key> wherever a respondent attribute can be named (sampling factors, external-input mappings, calibration targets). Extra top-level keys are ignored rather than stored, so a CRM export that sends city as its own field loses that value silently.

Surveys

Surveys track individual survey sessions and responses.

Method Endpoint Description
GET /api/v1/surveys List all surveys
POST /api/v1/surveys Create a new survey session
GET /api/v1/surveys/{id} Get survey by ID
PUT /api/v1/surveys/{id} Update survey status
DELETE /api/v1/surveys/{id} Delete a survey
GET /api/v1/surveys/{id}/current-step Current question + options + status (Survey Presentation Format)
POST /api/v1/surveys/{id}/responses Submit an answer; the next step is folded into the response
POST /api/v1/surveys/{id}/finish Explicitly complete the survey (idempotent)

Query Parameters:

  • questionnaire_id - Filter by questionnaire
  • campaign_id - Filter by campaign
  • respondent_id - Filter by respondent
  • status - Filter by status (pending, in_progress, completed)
  • expand=true - Include related objects

Survey responses carry status plus a derived, read-only completed boolean (true exactly when status is completed). Completion is expressed by updating status — there is no completed input or filter parameter.

Answering a survey through POST /api/v1/surveys/{id}/responses is limited to testing and simulation: a survey that has had an invitation sent is refused, so a live fielded interview cannot be written to programmatically. Answers submitted this way are recorded as machine-produced and their response times are excluded from speeder detection.

Respondent Pools

Respondent pools organize respondents into reusable groups for campaigns.

Method Endpoint Description
GET /api/v1/respondent-pools List all respondent pools
POST /api/v1/respondent-pools Create a new pool
GET /api/v1/respondent-pools/{id} Get pool by ID
PUT /api/v1/respondent-pools/{id} Update a pool
DELETE /api/v1/respondent-pools/{id} Delete a pool
POST /api/v1/respondent-pools/{id}/respondents Add respondents to pool
DELETE /api/v1/respondent-pools/{id}/respondents Remove respondents from pool

Query Parameters:

  • project_id - Filter by project

Sampling Strategies

Sampling strategies define target demographic distributions for creating representative samples.

Method Endpoint Description
GET /api/v1/sampling-strategies List all strategies
POST /api/v1/sampling-strategies Create a new strategy
GET /api/v1/sampling-strategies/{id} Get strategy by ID
PUT /api/v1/sampling-strategies/{id} Update a strategy
POST /api/v1/sampling-strategies/{id}/advance Advance a strategy: clone it and append reality-grounded outcome factors (the original is never edited)
DELETE /api/v1/sampling-strategies/{id} Delete a strategy

Query Parameters:

  • project_id - Filter by project

Example: Create a Sampling Strategy

curl -X POST https://portor.<tenant>.askalot.io/api/v1/sampling-strategies \
  -H "X-Api-Token: your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Representative Sample",
    "project_id": "550e8400-e29b-41d4-a716-446655440000",
    "target_size": 100,
    "factors": [
      {
        "name": "Gender",
        "attribute_path": "gender",
        "target_distribution": {"male": 0.48, "female": 0.50, "other": 0.02}
      },
      {
        "name": "Age Group",
        "attribute_path": "age",
        "factor_type": "bucketed",
        "target_distribution": {"18-34": 0.35, "35-54": 0.40, "55+": 0.25}
      }
    ]
  }'

Bundles

A Bundle binds one Project → one Questionnaire → a selected Campaign subset to a single linear Bronze → Silver → Gold dataset chain — the unit the Balansor pipeline board organizes around.

Method Endpoint Description
GET /api/v1/bundles List all bundles
POST /api/v1/bundles Create a new bundle
GET /api/v1/bundles/{id} Bundle detail, including its stage recipes (Silver Calibration Targets, Gold operations)
DELETE /api/v1/bundles/{id} Delete a bundle (and its dataset chain)
POST /api/v1/bundles/{id}/clone Clone a bundle — deep copy: ready datasets and stage recipes carry over
GET /api/v1/bundles/{id}/quality The bundle's Representativeness story (selection, fielded, weighted, fielding shift, response)
GET /api/v1/bundles/{id}/comparison Bronze-vs-Silver comparison (409 conflict until both stages are ready)
GET /api/v1/bundles/{id}/coding Open-end coding state — the coding candidates the Bronze offers, the analyst's question selection, and per question the proposed dimensions with their categories and selection state (deciding which dimensions become columns stays in the Balansor UI)
PUT /api/v1/bundles/{id}/coding/selection Set which open-text units this bundle codes — full replacement set of unit keys (manager role, identified user required)
POST /api/v1/bundles/{id}/strategy Assign or clear the bundle's current sampling strategy (manager role, identified user required)
PUT /api/v1/bundles/{id}/targets Edit the bundle's Calibration Targets — the Silver weighting spec (manager role, identified user required)

Query Parameters:

  • project_id - Filter by project

Stage operations (extraction, coding + weighting, refinement, export) are driven through the MCP Bundle/Dataset tools — see mcp-bundle-tools.md and mcp-dataset-tools.md for the full quality and stage parameter reference.

Response Format

Success Responses

All successful responses return JSON with standard HTTP status codes:

Status Meaning
200 OK Request succeeded (including resource creation — the created object is returned in the body)
204 No Content Resource deleted successfully

Error Responses

Errors use a single envelope: a machine-readable error code plus a human-readable detail:

{
  "error": "not_found",
  "detail": "bundle 'abc123' not found"
}
Status error code Meaning
401 unauthenticated Missing, invalid, or expired credentials
403 forbidden You lack the capability, or you don't own the target resource
404 not_found The resource does not exist (or is not visible to you)
409 conflict Valid request, but the resource isn't ready yet (e.g. comparing a bundle whose chain is still processing) — retry after the pipeline finishes
422 validation_error Malformed input; detail carries a list of field-level issues

OpenAPI Specification

The complete API specification is available in OpenAPI 3.0 format:

GET /api/v1/openapi.json

Use this specification to generate typed API clients in your preferred programming language.

Common Workflows

Automating Campaign Creation

# 1. Create a project
PROJECT_ID=$(curl -s -X POST https://portor.<tenant>.askalot.io/api/v1/projects \
  -H "X-Api-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Q1 Survey"}' | jq -r '.id')

# 2. Create a questionnaire (the QML itself is the payload — there is no file to stage)
QUESTIONNAIRE_ID=$(curl -s -X POST https://portor.<tenant>.askalot.io/api/v1/questionnaires \
  -H "X-Api-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg p "$PROJECT_ID" --rawfile c feedback.qml \
        '{name: "Customer Feedback", project_id: $p, content: $c}')" | jq -r '.id')

# 3. Create a campaign
curl -X POST https://portor.<tenant>.askalot.io/api/v1/campaigns \
  -H "X-Api-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"Q1 Campaign\", \"project_id\": \"$PROJECT_ID\", \"questionnaire_id\": \"$QUESTIONNAIRE_ID\"}"

Importing Respondents from Your CRM

# Import respondents from a CSV or your CRM system
for respondent in "${RESPONDENTS[@]}"; do
  curl -X POST https://portor.<tenant>.askalot.io/api/v1/respondents \
    -H "X-Api-Token: $TOKEN" \
    -H "Content-Type: application/json" \
    -d "$respondent"
done

Next Steps

  • MCP Interface


    Enable AI agents to interact with the platform

    MCP Docs

  • Creating Surveys


    Learn QML syntax for questionnaire design

    Guide

  • Campaign Management


    Configure and launch survey campaigns

    Targetor Guide