CCA

Tech Partner

Documentation

Partner DocumentationIntake and Scheduling APIDeveloper Guide

Developer Guide

Audience: Engineering teams implementing the Intake and Scheduling API. This integration calls CCA server-to-server to search providers, retrieve availability, and submit In-App Scheduling or Stand-Alone Intake requests so members remain on the partner platform. It requires more engineering work than the White-label Members Portal. Field-level rules are in Technical Terminology; post-submission behavior is in Case Lifecycle.

About This Integration

The Intake and Scheduling API lets an external partner platform call CCA directly, server-to-server, to look up available providers, list reference data such as valid regions, create bookings, and look up members it has previously created — using a persistent, CCA-issued Bearer token.

This page is the authentication and onboarding guide. It is separate from token-based authentication on the White-label Members Portal. There is no browser redirect and no token generation on the partner's side: CCA generates the token once during onboarding and provides it to the partner, who attaches it to every request until it is rotated or revoked.

Key Terms

For Provider and Member, see Basic Terminology. The terms below are specific to this API.

TermDefinition
RegionA state or province sub-unit used to match providers to members. Required for a country if the provider-regions endpoint returns more than one region for it (state/province-level regions, not just a single country-wide entry).
Bearer tokenThe persistent API credential CCA issues to your platform. You attach it as Authorization: Bearer <token> on every request — no token generation or signing required on your side.
BookingA scheduled session between a member and a specific provider at an agreed UTC time slot.
IntakeThe API payload name for Stand-Alone Intake: a request submitted without a specific provider or slot. CCA's provider services team finds a suitable clinician afterward.
external_user_idYour platform's own stable identifier for a member. CCA uses it (combined with your partner identity) to find-or-create the member record, preventing duplicates.

Setup

  1. Receive from CCA: the environment base URL and your Bearer token. This is the only credential you will handle — everything else CCA needs to identify you and your organization is resolved internally from the token.
  2. Store the Bearer token in your server-side secrets manager. Treat it as a production credential. This token must be attached to the Authorization: Bearer <token> HTTP header for all requests.
  3. Call the provider-regions endpoint to discover valid region_id values, then search availability and create a booking.
  4. Complete the sandbox testing checklist before production cutover.

What CCA Provisions (Before Go-Live)

ItemDescription
Bearer tokenA persistent token issued by CCA, delivered over an agreed secure channel (e.g. 1Password). This is the only credential you receive — your keyId and organizationId are resolved internally by CCA from this token and are never sent or seen by you. Sandbox and production use different tokens.
Environment URLsBase API URL per environment (sandbox vs. production).

Making Requests

Every endpoint uses the same authentication — no separate login or token-exchange step:

Authorization: Bearer <token>

There is no request body signing, no claims to construct, and nothing to generate. The token CCA gives you is used exactly as received, on every call, until it is rotated.

The Four Endpoints

EndpointPurpose
GET /partners/v1/provider-regions/List valid regions for a country — use this to discover the region_id values the availability search expects. Paginated.
GET /partners/v1/providers/Search provider availability by region_id or country_code, optionally filtered by modality/language. Paginated.
POST /partners/v1/booking-request/Find-or-create the member (by external_user_id) and submit a booking request — or, if you omit bookingData, submit a general intake for CCA to assign a provider to instead.
GET /partners/v1/user/Look up a previously-created member by the external_user_id you assigned them.

Full parameter and response documentation for each endpoint lives in the API Docs section.

Booking Example

A full walkthrough of the real sequence, from discovering a region through confirming the booking. Every call below sends the same Authorization: Bearer <token> header.

1. Discover a Region

GET /partners/v1/provider-regions/?country_code=US
[
  { "id": 1, "countryCode": "US", "stateProvinceCode": "US-NY", "stateProvinceNameLocal": "New York", "stateProvinceLabel": "State" },
  { "id": 2, "countryCode": "US", "stateProvinceCode": "US-CA", "stateProvinceNameLocal": "California", "stateProvinceLabel": "State" },
  { "id": 3, "countryCode": "US", "stateProvinceCode": "US-TX", "stateProvinceNameLocal": "Texas", "stateProvinceLabel": "State" }
]

This is one page of results — one entry per active region in the country, paginated (page/page_size, same as described above). Take the id you want — that is the region_id the next call expects.

2. Search Provider Booking Availability

GET /partners/v1/providers/?region_id=1

Send at least one of region_id/country_code region_id is required for a country if the provider-regions endpoint returns more than one region for it. Full parameter rules, error cases, and filter options (modality, language, pagination) will be documented in the API Docs section.

{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "providerId": 789,
      "details": { "firstName": "Jane", "lastName": "Smith", "...": "..." },
      "availability": [
        { "utcStartTime": "2026-08-02T14:00:00Z", "utcEndTime": "2026-08-02T14:45:00Z" }
      ]
    }
  ]
}

Pick a providerId and one of its availability slots. next/previous are full URLs — call them directly for the next/previous page, no need to build the query yourself.

3. Create the Booking

POST /partners/v1/booking-request/
Content-Type: application/json

Send the slot's utcStartTime/utcEndTime from step 2 exactly as received — no timezone conversion needed:

{
  "user": { "external_user_id": "patient-00123" },
  "bookingData": {
    "selfSchedulingProviderId": 789,
    "utcStartTime": "2026-08-02T14:00:00Z",
    "utcEndTime": "2026-08-02T14:45:00Z"
  },
  "memberData": {
    "email": "jane.doe@example.com",
    "firstName": "Jane",
    "lastName": "Doe",
    "dob": "1990-01-01",
    "gender": "Female",
    "maritalStatus": "Single",
    "clientType": "Employee",
    "leaveVoicemail": "Yes",
    "phoneNumber": "+15551234567",
    "streetAddress": "123 Main St",
    "suite": "",
    "city": "New York",
    "state": "NY",
    "zipCode": "10001",
    "country": "US",
    "reasonForSeekingSupport": "Anxiety",
    "preferredModality": "Video Counseling",
    "preferredSpokenLanguage": "en"
  },
  "phq9Data": [
    { "question": "PHQ-9 1.Little interest or pleasure in doing things.", "answer": "0 Not at all" }
  ]
}

memberData.preferredSpokenLanguage is optional, a BCP-47 code (e.g. en, en-US, fr-CA). Defaults to the en language code if omitted.

A 201 response confirms the booking:

{
  "id": 456,
  "providerId": 789,
  "phoneNumber": "+15551234567",
  "preferredModality": "Video Counseling",
  "member": { "id": 42, "firstName": "Jane", "email": "jane.doe@example.com" },
  "utcStartTime": "2026-08-02T14:00:00Z"
}

user.external_user_id is your own stable id for this member — calling this endpoint again with the same value updates the same member record (matched on email/name/etc.) and adds another booking, rather than creating a duplicate.

No specific provider or slot yet? Omit bookingData entirely (send only user, memberData, and phq9Data) and this endpoint submits a general intake instead of a booking — our operations team assigns a provider afterward. The response in that case has providerId/acceptedAt/declinedAt/utcStartTime all null (nothing was scheduled yet — once a provider is assigned, this same shape starts returning real values):

{
  "id": 245,
  "providerId": null,
  "authorizationNumber": null,
  "phoneNumber": "+15551234567",
  "preferredModality": "Video Counseling",
  "acceptedAt": null,
  "declinedAt": null,
  "member": { "id": 42, "firstName": "Jane", "email": "jane.doe@example.com" },
  "utcStartTime": null
}

4. Confirm the Member

Now that the booking exists, you can look the member up again by the same external_user_id — this confirms CCA created (or matched to an existing) member record correctly, and gives you back the member's CCA id for your own records.

GET /partners/v1/user/?external_user_id=patient-00123
{
  "id": 42,
  "firstName": "Jane",
  "email": "jane.doe@example.com",
  "userType": "customer",
  "lastName": "Doe",
  "country": "US",
  "preferredSpokenLanguage": "English",
  "preferredSpokenLanguageCode": "en",
  "externalUserId": "patient-00123"
}

Sample Code

Unlike token-based authentication on the members portal, there is no cryptography to implement on the partner side — you send a standard Authorization header.

Python — using requests:

import requests

BASE_URL = "https://api.ccaplatform.com"
BEARER_TOKEN = "<the token CCA gave you>"

def list_regions(country_code: str) -> list:
    response = requests.get(
        f"{BASE_URL}/partners/v1/provider-regions/",
        params={"country_code": country_code},
        headers={"Authorization": f"Bearer {BEARER_TOKEN}"},
    )
    response.raise_for_status()
    return response.json()

Node.js — using fetch:

const BASE_URL = 'https://api.ccaplatform.com';
const BEARER_TOKEN = '<the token CCA gave you>';

async function listRegions(countryCode) {
  const response = await fetch(
    `${BASE_URL}/partners/v1/provider-regions/?country_code=${countryCode}`,
    { headers: { Authorization: `Bearer ${BEARER_TOKEN}` } },
  );
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response.json();
}

Token Format

You do not need to construct or decode this token — CCA issues it as an opaque value and you use it as-is. For reference: it is a JWT signed with HS256. Its kid header — short for Key ID, the standard JWT field identifying which key signed a token — carries the same keyId value mentioned above, which CCA uses to look up which partner the token belongs to. It carries no expiry (exp) — it is a static credential, valid until CCA rotates or revokes it, not a short-lived session token.

Security and Privacy

ControlDescription
Persistent, revocable credentialThe token has no built-in expiry; CCA can deactivate your partner record at any time to instantly revoke access without deleting configuration.
Encryption in transitAll requests must be made over HTTPS.
Safe error responsesAuthentication failures return a stable error code and a generic message — internal configuration details (e.g. which secret is misconfigured) are never included in the response body, only in CCA's internal logs.
Member data namespacingMembers you create are looked up by combining your partner identity with the external_user_id you assign — two different partners can safely use the same external_user_id values without ever colliding.

What Partners Must Do on Their Side

  • Store the Bearer token only on trusted servers — never in a browser or mobile client. This token must be attached to the Authorization: Bearer <token> HTTP header for all requests.
  • Do not log, cache, or persist the token outside your secrets manager.
  • Treat the sandbox token and production token as separate credentials.
  • Notify CCA immediately if you suspect the token has been exposed.

Errors

CodeHTTPMeaning
token_invalid401Missing/malformed Authorization header, malformed token, or signature verification failed
missing_claim401Token is missing its required internal kid header
partner_not_found401Token does not resolve to a known partner
partner_inactive401Partner has been deactivated by CCA
400Request validation failure (missing/invalid parameters); response body is {"message": "...", "status": 400}
403Your organization has been deactivated; response body is {"message": "...", "status": 403}
409Your partner record has no organizationId configured (contact CCA); response body is {"message": "...", "status": 409}

The 401 responses share the shape {"code": "...", "message": "..."}. The code is always one of the stable values above — the message is a safe, generic description; it deliberately never reveals internal configuration details.

Secret Rotation

Notify your CCA onboarding lead to request a new Bearer token. CCA generates the replacement and coordinates a cutover window with you — swap to the new token at that time. There is no overlap window: only one token is active per partner at a time.

If you suspect your token has been compromised, notify CCA immediately for emergency rotation.

Sandbox Testing Checklist

  1. Confirm CCA sent your sandbox Bearer token and base URL.
  2. Store the sandbox token in your server-side vault.
  3. Call GET /partners/v1/provider-regions/ to confirm authentication works end-to-end.
  4. Search availability and submit a test booking via POST /partners/v1/booking-request/.
  5. Confirm you can look the resulting member up again via GET /partners/v1/user/.
  6. Call GET /partners/v1/providers/?country_code=<your country> and confirm it returns providers without sending region_id.
  7. Add &language=<a BCP-47 code> to an availability search and confirm results narrow to providers who speak it.
  8. Test pagination — call with page_size=1 (or similar) and confirm count/next/results behave as expected; then call an out-of-range page and confirm you get a 400, not a raw error.
  9. Call POST /partners/v1/booking-request/ with bookingData omitted and confirm you get an intake response (providerId/utcStartTime both null) instead of a booking.
  10. Confirm a malformed or incorrect token returns a 401 with a token_invalid code, not a raw error.

Support

Technical integration questions: techsupport@ccainc.com

For operational provisioning (token rotation, organization configuration), contact your CCA onboarding lead.