Getting Started

Pick the right token, make your first REST request and see how authentication works for scripts, AI agents and third-party apps.

Prerequisites

Before you start, you need:

  • A Disqua account — sign up free
  • A workspace (created during onboarding)
  • curl or any HTTP client (Postman, Insomnia, etc.)

All API calls require HTTPS. HTTP requests will be redirected with a 301 to the HTTPS equivalent.

Step 1 — Get a token

Disqua uses Bearer token authentication. Pick the credential that fits what you are building:

  • A quick script acting as you — log in and use the returned JWT access_token (valid 15 minutes; renew it with the refresh_token).
  • An AI agent or long-running automation — in the console open Profile → AI access, create a master token (dq_usr_…), then exchange it for a session key (dq_ai_…) scoped to one workspace with POST /v1/ai/request-session-key. The master token is shown once.
  • An app other people connect to their Disqua — use OAuth 2.1 (see below). Access tokens start with dq_oat_.
  • Just posting into a channel from another system — you need no token at all: create an incoming webhook.
Request POST /v1/auth/login
curl -X POST https://api.disqua.com/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "jane@example.com", "password": "YOUR_PASSWORD" }'

# → { "ok": true, "data": { "user": { … }, "access_token": "eyJ…", "refresh_token": "…" } }
# Accounts with 2FA get { "requires_2fa": true, "temp_token": "…" } —
# finish with POST /v1/auth/2fa/challenge { "temp_token": "…", "code": "123456" }.

Treat every token like a password. Never commit it to version control. Use environment variables or a secrets manager.

Step 2 — Make your first request

Verify your token by fetching your own profile:

Request GET /v1/users/me
curl https://api.disqua.com/v1/users/me \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
Response 200 OK
{
  "ok": true,
  "data": {
    "user": {
      "id": "7f9e…",
      "username": "janedoe",
      "display_name": "Jane Doe",
      "email": "jane@example.com",
      "avatar_url": null,
      "status": "online"
    },
    "preferences": { … }
  }
}

List channels in a workspace

Every resource lives under a workspace. Get your workspace_id from GET /v1/workspaces (add ?slug=your-slug to match one exactly), then:

Request GET /v1/workspaces/:workspaceId/channels
curl "https://api.disqua.com/v1/workspaces/WORKSPACE_ID/channels" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

# → { "ok": true, "data": { "channels": [ { "id": "7a0c…", "name": "general", "type": "public", … } ] } }

Send a message

Request POST /v1/workspaces/:workspaceId/channels/:channelId/messages
curl -X POST "https://api.disqua.com/v1/workspaces/WORKSPACE_ID/channels/CHANNEL_ID/messages" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Hello from the Disqua API!" }'

# 201 → { "ok": true, "data": { "message": { "id": "8b1d…", "content": "Hello from the Disqua API!",
#                                            "content_html": "<p>Hello from the Disqua API!</p>", … } } }

OAuth 2.1 for third-party apps

Standard authorization code flow with PKCE (S256). Endpoints are published at https://api.disqua.com/.well-known/oauth-authorization-server; public clients can register themselves through dynamic client registration (POST /v1/oauth/register). Supported scopes: workspaces:read, channels:read, messages:read, messages:write, helpdesk:read, helpdesk:write, reminders:write, search:read.

Step 1 — Send the user to the consent screen
GET https://console.disqua.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=channels:read+messages:read
  &state=random_csrf_string
  &code_challenge=BASE64URL(SHA256(code_verifier))
  &code_challenge_method=S256
Step 2 — Exchange the code for tokens
POST https://api.disqua.com/v1/oauth/token
Content-Type: application/x-www-form-urlencoded   (JSON is accepted too)

grant_type=authorization_code
&code=AUTH_CODE_FROM_REDIRECT
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=YOUR_CODE_VERIFIER

# → { "access_token": "dq_oat_…", "token_type": "Bearer", "expires_in": …,
#     "refresh_token": "…", "scope": "channels:read messages:read" }

Users see and revoke connected apps in the console under Profile → Apps.

Errors

All errors use the same envelope:

{
  "ok": false,
  "error": {
    "code": "E_AUTHZ_NOT_MEMBER",
    "message": "You are not a member of this workspace.",
    "status": 403
  }
}
HTTP Typical code Meaning
400 / 422E_VALIDATION_FAILED, E_VALIDATION_BAD_REQUESTBody or parameters failed validation
401E_AUTH_MISSING, E_AUTH_TOKEN_INVALID, E_AUTH_TOKEN_EXPIREDNo token, a bad one, or an expired one — refresh it
402E_PLAN_FEATURE_NOT_AVAILABLEThe workspace plan does not include this feature or limit
403E_AUTHZ_NOT_MEMBER, E_AUTHZ_INSUFFICIENT_ROLE, E_AUTHZ_FORBIDDENNot a member, wrong role, or the token's scope does not allow it
404E_NOT_FOUND, E_CHANNEL_NOT_FOUND, …Does not exist or is not visible to you
429—Rate limited (default 600 requests/min per user) — back off and retry
500E_INTERNALUnexpected server error — contact support with the X-Request-ID response header

Next steps