# Integrate with the Portal API

Use your own assessment interface while keeping clients, reports and credits in your Portal account. The API is server-to-server: keep the key on your server, never in browser or mobile code. Use the backend address supplied for your environment followed by `/partner/v1`. Portal v2 is the product version; `/partner/v1` is the API version.

## Set up access

In Settings → Developer, name your key and create it. New keys support the full integration, including connection checks. Older connection-only keys retain their limited permissions; create a new key if you need assessment and report access. Integration keys can update clients, manage API-created assessments, read all entitled full reports and spend credits in their account. Five keys may be active at once; each expires after 90 days. Revoke a key to stop its access.

Send `Authorization: Bearer YOUR_KEY` with every request. No provider ID is needed. Requests are limited to 60 per key per minute; after 429, wait for the `Retry-After` interval. Keys also stop working when their issuing owner or account loses access.

## Complete the flow

1. Check `GET /status` for connectivity and permissions.
2. Send `PUT /participants` with `fullName`, `email`, `locale` and optional `externalId`. Save `participant.id`. Matching email updates the existing client in your account. Archived clients are rejected; changing an email creates a different client.
3. Send `POST /assessments` with `participantId`, `locale` and `expiresAt`, plus a unique `Idempotency-Key` header. Expiry must be in the future, within 180 days. Save `assessment.id`.
4. Fetch `GET /assessments/{assessmentId}/questions`. Render the returned text and choices in order. There are 120 questions, available in English, Arabic or Spanish.
5. Save actual responses with `PUT /assessments/{assessmentId}/answers`. The body is an `answers` array of objects with `questionId` and integer `value` from 1 to 5. Send 1–120 unique question IDs per request. The API checks the IDs and upserts answers; it does not accept invented questions.
6. Once all answers are saved, send `POST /assessments/{assessmentId}/complete` with an empty JSON object. Save `result.id` as the assessment result ID. `GET /assessments/{assessmentId}` also returns that ID after completion.
7. Check `GET /credits`, then send `POST /reports` with `assessmentResultId` and a separate `Idempotency-Key`. Save `report.id`. A new permanent report consumes **1 credit**. Assessment creation and completion are free.
8. Read `GET /reports/{reportId}`. Add `?locale=en`, `?locale=ar` or `?locale=es` for a localized view. Reading is free and does not change the saved language. The response uses the same approved report content and presentation as the owner dashboard; there is no fixed metric-count promise.

The assessment endpoints manage sessions created by this API. Report reads may access all full reports entitled to the account, including those created in the normal Portal workflow.

## Retry without duplicate charges

Use an `Idempotency-Key` of 8–128 letters, digits, colons, underscores or hyphens. Reuse the same key and body after a failed assessment/report creation request. A different operation needs its own key. A changed body under the same key returns 409. Save your request IDs alongside the returned client, assessment, result and report IDs.

Repeating completion returns the saved result. Asking for a report that already exists returns that report without another charge, even with a new request ID. If generation fails, the normal credit reservation is released. Resolve the error and retry with the original ID. A failed response may follow a successful save, so do not assume every retry needs a new ID.

An incomplete assessment returns 409; missing credits return 402; an expired assessment returns 410. Missing or foreign-account records return 404. Invalid or revoked keys return 401; insufficient permissions return 403. JSON request bodies must not exceed 32 KiB. Unexpected input fields are rejected. All responses use `Cache-Control: no-store`.

## Where the data goes

Clients and reports appear in the normal workspace; credit spending appears on the Credits page. Names and emails use the existing client record. `externalId` is your account-scoped integration reference: omitted values preserve the existing reference; supplied values may update it. It is deliberately omitted from recipient reports, emails and PDFs. Assessment locale controls the questionnaire and original report language; a read-time locale only changes the returned view.

This flow sends no invitation/report emails and creates no public sharing link. Sharing remains a separate owner action. Do not use API completion to bypass the invitation or automatic-delivery workflow.

## Examples and downloads

The Developer page shows copyable **Bash/cURL** examples. Set `PORTAL_API_BASE`, `PORTAL_API_KEY`, each returned ID, the expiry timestamp and separate creation request IDs before continuing. Provide an `answers.json` file containing actual participant responses.

Import the [Postman collection](/developer/portal-partner-v1.postman.json) and configure `baseUrl` (including `/partner/v1`) and `apiKey` locally. Set `expiresAt` and `answersJson`; the collection captures returned IDs and initializes missing request IDs. Keep IDs for retries, and clear them when starting a new assessment/report. Running the report-generation request spends a credit. Do not share exports containing your secret or client data.

See the [API reference (English)](/developer/portal-partner-v1.reference.html) and [OpenAPI specification](/developer/portal-partner-v1.openapi.json) for request and response fields. Older provider-based API references describe a different contract and do not apply to current Portal accounts.
