The REST API is Junior's organization-level integration surface, for IT and engineering teams. It authenticates with an API key the Junior team issues to your company, not with a person's sign-in. To work with your own calls from an AI assistant instead, use the MCP connector; it needs no code.

Send every request to https://api.junior.ai/v1 with the key as a bearer token: Authorization: Bearer <your key>. Reading calls needs the REST API switched on for your organization. Endpoints marked write access also need write access switched on; without it they return 403 with code write_not_enabled.

This page describes API version 1.11.0.

Endpoints

Calls

Read call (interview) records, metadata, and transcripts, and ingest calls from sources Junior has no direct integration with. List first, then fetch a single call by call_id for the full record. Ingestion requires the organization to be enabled for the write surface.

EndpointWhat it doesAccess
GET /v1/callsList callsRead
POST /v1/callsIngest a callWrite
GET /v1/calls/{call_id}Get a single callRead

Projects

Create, edit, archive, and unarchive projects, and look up the billing offices a project can be assigned to. Requires the organization to be enabled for the write surface.

EndpointWhat it doesAccess
GET /v1/billing-officesList billing officesWrite
GET /v1/projectsList projectsWrite
POST /v1/projectsCreate a projectWrite
GET /v1/projects/{project_id}Get a projectWrite
PATCH /v1/projects/{project_id}Update a projectWrite
POST /v1/projects/{project_id}/archiveArchive a projectWrite
POST /v1/projects/{project_id}/unarchiveUnarchive a projectWrite

Errors

Every error response has the same JSON body. error.code is the field to branch on; quote error.request_id when you contact support.

FieldTypeDescription
errorobject—
error.code"bad_request" or "unauthorized" or "forbidden" or "rest_not_enabled" or "write_not_enabled" or "write_not_configured" or "not_found" or "payload_too_large" or "idempotency_key_in_progress" or "idempotency_key_reused" or "rate_limited" or "internal_error"—
error.messagestring—
error.request_idstring—

List calls

GET /v1/calls · read access

Returns a paginated list of calls (ID and duration) within a date range. Calls are ordered by created_at DESC with id as a stable tiebreaker. Which call types are returned is set per organization; by default only interviews (expert calls) are returned.

Query parameters

ParameterTypeDescription
end_date requiredstring (date-time)Inclusive upper bound (RFC 3339 date-time). UTC (...Z) or a numeric offset (...-05:00) are both accepted.
pageinteger (min 1) · default 1—
per_pageinteger (1–100) · default 25—
project_idstring (uuid)Filter to calls in this project (UUID from GET /v1/projects). A project outside your organization returns no calls.
start_date requiredstring (date-time)Inclusive lower bound (RFC 3339 date-time). UTC (...Z) or a numeric offset (...-05:00) are both accepted.
user_emailstring (email)Filter to calls created by the user with this email.

Response 200

Paginated list of call IDs.

FieldTypeDescription
dataarray—
data[].call_idstringExternal call identifier (UUID).
data[].duration_secondsinteger or nullCall duration in whole seconds, derived from the call recording/transcript. null when the duration is unknown.
data[].project_idstring or nullUUID of the Junior project this call belongs to — the same project_id used by GET /v1/projects/{project_id}. null when the call is not in a project.
paginationobject—
pagination.pageinteger—
pagination.per_pageinteger—
pagination.total_itemsinteger—
pagination.total_pagesinteger—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
429Too Many Requests — rate limit exceeded.

Ingest a call

POST /v1/calls · write access

Ingests a call from a source Junior has no direct integration with. Creation only — this never modifies an existing call. Returns 202: the call is created asynchronously and appears in GET /v1/calls once processing starts, so poll rather than expecting it in the response. Requires the organization to be enabled for the write surface.

Request body

A JSON object:

FieldTypeDescription
attendee_emailsarray of stringPeople to invite to the call; they can then see it even when it has no project. Emails that don't resolve to users in your organization are skipped rather than rejected. You must supply this OR project_id: a call with neither has no project, no creator (API keys authenticate as an organization, not a user) and no invitees, so it appears in no call list at all — not even Unmatched — and the request is rejected with 400.
datestring (date-time)When the call took place (RFC 3339). Either UTC (2026-01-02T10:00:00Z) or a numeric offset (2026-01-02T10:00:00-05:00) — both resolve to the same instant. A local time with no offset is rejected as ambiguous.
expert_organizationstring (max 255)The expert's employer — NOT your organization.
external_idstring (1–255)Your own stable identifier for this call, and the deduplication key: re-sending the same external_id is a no-op rather than a second call. STRONGLY RECOMMENDED. When omitted, a fingerprint is derived from the call's content and identity fields instead — which means a corrected re-send is treated as a new call.
job_titlestring (max 255)—
name requiredstring (1–255)The expert's name.
project_idstring (uuid)Project to file the call under; every member of that project can then see the call. Rejected with 400 if the project isn't in your organization. REQUIRED unless you supply attendee_emails — see the note on that field.
sourcestring (max 255)Where the call came from, e.g. the expert network's name. Recorded as the call's source.
transcript requiredstringThe transcript text. Plain text or markdown — not a file upload.

Response 202

Accepted — the call is queued for creation and processing.

FieldTypeDescription
dataobject—
data.external_idstringThe deduplication key used — the one you supplied, or the derived fingerprint if you omitted it. Reuse it to make a retry idempotent.
data.project_idstring or nullEchoes the project the call was filed under, or null when it was left unmatched.
data.status"processing"—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
413Payload Too Large — the call exceeds the maximum accepted size. The transcript dominates this, and the limit applies to the JSON-serialized call, so escape-heavy text (many newlines or quotes) reduces the transcript length that fits.
429Too Many Requests — rate limit exceeded.

Get a single call

GET /v1/calls/{call_id} · read access

Returns interview metadata, optional summary, and one or more transcript entries. Which entries appear depends on the request flags and your organization's configuration.

Path parameters

ParameterTypeDescription
call_id requiredstring—

Query parameters

ParameterTypeDescription
include_cleaned_transcriptboolean · default falseInclude the cleaned transcript entry — question/answer pairs with lightly rewritten answers. This is original-language, non-redacted content, so it is returned only when your organization is permitted the cleaned_transcript field and cleaned Q&A data exists for the call. Otherwise the entry is omitted with no error. Because the redacted entry is on by default, a cleaned request from an organization that lacks this field returns only the redacted entry — but only when that entry is itself permitted and available. Otherwise the response contains no transcript entries.
include_original_languageboolean · default falseReserved for v2 (translation support); accepted but ignored in v1, where every transcript entry is already original-language.
include_redacted_transcriptboolean · default trueInclude the redacted transcript entry. Enabled by default. Returned only when your organization is permitted the redacted_transcript field and the call is fully anonymized. Otherwise the entry is omitted with no error.
include_summaryboolean · default falseInclude data.summary in the response.
include_transcriptboolean · default falseInclude the primary, non-redacted transcript entry. This is original-language content, so it is returned only when your organization is permitted both the original_transcript and original_language fields. Otherwise the entry is omitted with no error.

Response 200

Call detail.

FieldTypeDescription
dataobject—
data.call_idstring—
data.call_type"interview" or "meeting"The type of call. interview for an expert/research interview, meeting for an internal meeting.
data.conducted_by_emailstring or nullEmail of the user who conducted the call (the transcript creator). Present only when your organization is configured to return participant emails. null if that user's profile is unavailable.
data.created_atstring (date-time) or null—
data.duration_secondsinteger or nullCall duration in whole seconds, derived from the call recording/transcript. null when the duration is unknown.
data.expert_network_participant_emailsarray of stringParticipant email addresses associated with the expert-network (EN) call: your own team members on the call plus any other attendees the provider reported. Does NOT include the expert's own email — Junior does not store it. Present only when your organization is configured to return participant emails. Empty array for calls that did not originate from an EN integration.
data.interviewobject—
data.interview.billing_codestring or nullThe project's billing/engagement code (labeled "Project Code" in the Junior UI by default). Returns null unless your organization is configured to return billing codes (and the call's project has one set).
data.interview.billing_officestring or nullResolved billing-office display name for the call's project. Returns null unless your organization is configured to return billing offices (and the project has a billing office assigned).
data.interview.countrystring or null—
data.interview.created_atstring (date-time) or null—
data.interview.job_titlestring or null—
data.interview.namestring or nullSpeaker name. Returns the anonymized pseudonym unless your organization is configured to return original (non-redacted) transcripts.
data.interview.organizationstring or null—
data.interview.ratinginteger or null—
data.interview.sectorstring or nullReserved. Returns null in v1.
data.interview.segmentstring or null—
data.project_idstring or nullUUID of the Junior project this call belongs to — the same project_id used by GET /v1/projects/{project_id}. null when the call is not in a project.
data.summarystringPresent only when include_summary=true (and only if your organization returns summaries).
data.transcriptsarray—
data.transcripts[].cleanedbooleantrue for the cleaned entry — the original-language, unredacted transcript restructured as question/answer pairs with lightly rewritten answers.
data.transcripts[].is_primarybooleanExactly one entry has is_primary: true — the original-language, unredacted version of the call.
data.transcripts[].languagestring or nullDetected language of this transcript entry as a short language code (e.g. en, fr), or null when detection did not run.
data.transcripts[].redactedboolean—
data.transcripts[].textstring—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
404Not Found — call_id does not exist or is outside the access window.
429Too Many Requests — rate limit exceeded.

List billing offices

GET /v1/billing-offices · write access

Lists your organization's billing offices — the valid values to pass as billing_office_name when creating a project. Returns names only (plus which is the default), never internal IDs.

Response 200

The organization's billing offices.

FieldTypeDescription
dataarray—
data[].is_defaultboolean—
data[].namestring—

Errors

StatusMeaning
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
429Too Many Requests — rate limit exceeded.

List projects

GET /v1/projects · write access

Lists your organization's projects, newest first. Use it to find a project_id — for example the one to pass to POST /v1/calls. Returns a lean summary per project; fetch GET /v1/projects/{project_id} for the full record. Requires the organization to be enabled for the write surface.

Scoped to the ORGANIZATION, not to a user: an API key authenticates an organization rather than a person, so every project in the organization is listed.

Query parameters

ParameterTypeDescription
namestring (1–255)Filter to projects with this exact name (case-insensitive). Not a substring or pattern match.
pageinteger (min 1) · default 1—
per_pageinteger (1–100) · default 25—

Response 200

Paginated list of projects.

FieldTypeDescription
dataarray—
data[].billing_codestring or nullReturns null unless your organization is configured to return billing codes. Gated identically to the detail endpoint.
data[].created_atstring (date-time) or null—
data[].idstring (uuid)Project UUID. Pass as project_id elsewhere.
data[].namestring—
data[].sectorstring or null—
paginationobject—
pagination.pageinteger—
pagination.per_pageinteger—
pagination.total_itemsinteger—
pagination.total_pagesinteger—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
429Too Many Requests — rate limit exceeded.

Create a project

POST /v1/projects · write access

Creates a project for the authenticated key's organization. Requires the organization to be enabled for the write surface.

Retries are safe by default. Send the same body twice within 24 hours and the second request replays the first one's 201 instead of creating a second project — no header needed. This matters because there is no way to delete a project through this API, so a duplicate needs manual cleanup on our side.

Send an Idempotency-Key header when you want to control that yourself: two different keys mean two deliberate creates, even with identical bodies.

Emails that junior has never seen are fine. They don't need an account yet: the request stages a pending project invitation with the listed role, reported in invited_lead_emails / invited_member_emails. No account is created and no invitation email is sent; they join through normal supported sign-in. Invitations expire after one year. The first listed lead is the designated creator. If that person is pending, the creator remains unset until their lead membership is granted; other leads signing in first do not become the creator. API invitations do not override existing SSO, valid web-invitation, or registered email-domain organization mappings; conflicting API invitations cannot arbitrarily choose an organization. An email that already belongs to a different organization is skipped for members and rejected for leads.

Headers

HeaderTypeDescription
Idempotency-Keystring (1–255)Optional. Deduplication is on whether or not you send this — omit it and the key is derived from the request body — so the header is an override, not the thing that switches protection on. An opaque, caller-generated value (max 255 printable ASCII characters, no whitespace; must not begin with auto:, which is reserved for derived keys). Retrying with the same key and the same body returns the ORIGINAL 201 and its response body. Reusing a key with a different body is rejected with 422; retrying while the first attempt is still in flight returns 409. Keys are scoped to your organization and this endpoint. Reach for it in two cases. To create two projects with identical bodies on purpose: give them different keys, since without a key identical bodies are treated as one create. Or to keep a create replay-safe for longer than 24 hours: a key you supply keeps replaying for at least 7 days, while a derived one stops replaying after 24 hours so that a genuinely later identical create still works. Equality is semantic, not byte-for-byte: the stored response is held as JSON, so a replay is the same JSON document — object-key order and whitespace are not guaranteed to match the original bytes.

Request body

A JSON object:

FieldTypeDescription
billing_codestringClient-facing engagement/charge code (the “Project Code”). Optional.
billing_office_namestringBilling office by name (case-insensitive; must exist in your organization). Resolved server-side to the office and its billing policy — you never supply the internal ID. Rejected with 400 if the name doesn't match an office.
budgetnumber (min 0)—
lead_emails requiredarray of stringProject leads (at least one), each added with the lead role when already in the organization. The first email is the designated project and running-summary creator. If that person is unknown, their pending invitation retains the designation and fills the creator fields when lead membership is granted. Other leads never take their place; removing the pending invitation cancels the designation. No account is created and no invitation email is sent. If an email also appears in member_emails, lead wins.
member_emailsarray of stringAdditional members added with the member role. An email junior has never seen receives a pending project invitation, with no account created and no invitation email sent. An email already in another organization is skipped. Leads are never double-added and win if named in both lists.
name requiredstring (1–255)—
sectorstring—
timezonestringOptional. Not stored — a US IANA timezone (e.g. America/New_York) sets the project locale to en_us; omitted leaves the default locale.

Response 201

Project created.

FieldTypeDescription
dataobject—
data.billing_codestring or null—
data.billing_office_namestring or nullThe resolved billing office name, or null if none.
data.created_atstring (date-time) or null—
data.idstring (uuid)—
data.invited_lead_emailsarray of stringRequested lead_emails junior had never seen: pending LEAD project invitations, not memberships. No account is created and no invitation email is sent; the person joins through normal supported sign-in. Invitations expire after one year.
data.invited_member_emailsarray of stringRequested member_emails junior had never seen: pending MEMBER project invitations, not memberships. No account is created and no invitation email is sent; the person joins through normal supported sign-in. Invitations expire after one year.
data.lead_emailsarray of stringThe lead users applied as memberships — the subset of requested lead_emails that resolved in the org.
data.member_emailsarray of stringThe member users applied as memberships — the subset of requested member_emails that resolved in the org (emails already in another organization are skipped). Never-seen emails land in invited_member_emails instead until they first sign in.
data.namestring—
data.sectorstring or null—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
409Conflict. Either an identical create is still in flight (matched on your Idempotency-Key, or on the request body if you didn't send one), or this attempt held the claim so long that another request took it over — in which case nothing was created and the attempt was rolled back. Retry in both cases: the retry replays the response of whichever attempt completed, or proceeds as a fresh create if the earlier one was abandoned (after 60 seconds).
422Unprocessable Entity — this Idempotency-Key was already used with a different request body. Retry with the original body, or use a new key. The stored response is never served to a request that doesn't match it. Only reachable if you send the header: without one, a different body is a different key.
429Too Many Requests — rate limit exceeded.
503Service Unavailable — the write surface is not configured for this deployment.

Get a project

GET /v1/projects/{project_id} · write access

Returns a project your organization owns — use it to confirm the server-resolved values (billing_office_name, applied memberships, and durable pending invitations) or to re-read a project whose create response you lost.

Scope: your API key identifies an organization, not a user, so this returns any project in your organization — including ones created in the Junior web app. Some sensitive fields (billing code, billing office, membership emails, and pending invitation emails) may be omitted depending on your organization's configuration, exactly as on the call endpoints.

This endpoint needs an id, so it can't recover a create that never returned one. For that, re-send the create: within 24 hours it replays the original 201 rather than making a second project. Requires the organization to be enabled for the write surface.

Path parameters

ParameterTypeDescription
project_id requiredstring (uuid)—

Response 200

Project detail. Field availability depends on your organization's configuration — see the schema.

FieldTypeDescription
dataobjectA project read back via GET /v1/projects/{project_id}. Same fields as the create response, except that some sensitive fields may be omitted depending on your organization's configuration. An API key is an organization credential, so this can return a project nobody using that key created.
data.billing_codestring or nullReturns null unless your organization is configured to return billing codes.
data.billing_office_namestring or nullThe resolved billing office name, or null if none. Returns null unless your organization is configured to return billing offices.
data.created_atstring (date-time) or null—
data.idstring (uuid)—
data.invited_lead_emailsarray of stringActive pending LEAD project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.invited_member_emailsarray of stringActive pending MEMBER project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.is_archivedbooleanWhether the project is archived. Set with POST /v1/projects/{project_id}/archive and POST /v1/projects/{project_id}/unarchive.
data.lead_emailsarray of stringThe lead users applied. Present only when your organization is configured to return participant emails — omitted entirely otherwise, so an absent key is indistinguishable from an empty list. Sorted alphabetically: the create echoes emails in request order, which isn't recoverable on a re-read. Pending lead invitations are listed separately in invited_lead_emails.
data.member_emailsarray of stringThe member users applied. Same participant-email gate and ordering as lead_emails.
data.namestring—
data.sectorstring or null—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
404Not Found — no project with this id in your organization. Deliberately indistinguishable from a project that belongs to another organization.
429Too Many Requests — rate limit exceeded.

Update a project

PATCH /v1/projects/{project_id} · write access

Edits a project your organization owns after it was created — rename it, change its billing fields or budget, add leads and members, or remove members. Requires the organization to be enabled for the write surface.

Every field is optional; send only what you want to change. name renames the project and can't be cleared. For billing_code, billing_office_name, and budget: a value sets it, null clears it, and omitting it leaves it untouched.

lead_emails and member_emails are add-only. Listed users are added with that role; anyone already on the project (in any role) is skipped, so re-sending is safe and never changes a role. An email junior has never seen receives a pending project invitation with the listed role; no account is created and no invitation email is sent. Re-adding a pending lead never downgrades it; adding a pending member as a lead promotes it. An explicit API assignment adopts any existing pending web invitation for that email and project into automatic acceptance on first web sign-in, preserving its token. Web invitations not assigned through the API still require explicit acceptance.

remove_member_emails removes matching memberships and cancels matching pending invitations, whatever their role. Removing the project creator hands creator ownership to the most recently added remaining lead, so a single call with the new lead in lead_emails and the old one in remove_member_emails transfers the project. If no other lead with an account remains, removing the creator is rejected with 400. An email can't appear in both an add list and remove_member_emails, and can't be in both lead_emails and member_emails — either is rejected with 400.

The response echoes the project's durable state after the edit, gated per field exactly like GET /v1/projects/{project_id}.

Path parameters

ParameterTypeDescription
project_id requiredstring (uuid)—

Request body

A JSON object:

FieldTypeDescription
billing_codestring (max 255) or nullClient-facing engagement/charge code. Send a value to set it, null to clear it, or omit to leave it unchanged.
billing_office_namestring (1–255) or nullBilling office by name (case-insensitive; must exist in your organization). Resolved server-side to the office and its billing policy. Send a value to set it, null to clear it, or omit to leave it unchanged. Rejected with 400 if a non-null name doesn't match an office.
budgetnumber (min 0) or nullSend a value to set it, null to clear it, or omit to leave it unchanged.
lead_emailsarray of stringUsers to ADD with the lead role. A never-seen email receives a pending LEAD project invitation; no account is created and no invitation email is sent. Adding a pending member as a lead promotes it, while a pending lead is never downgraded. Anyone already on the project is skipped.
member_emailsarray of stringUsers to ADD with the member role. An email junior has never seen receives a pending project invite (no account created — the person joins as a member on first sign-in). Anyone already on the project is skipped.
namestring (1–255)New project name. Leading and trailing whitespace is trimmed. Can't be cleared: null or a blank name is rejected with 400.
remove_member_emailsarray of stringEmails to REMOVE from the project, whatever their role. Matching pending invitations are cancelled too, including for people who have never signed in. Removing the project creator hands creator ownership to the most recently added remaining lead; it is rejected with 400 when no other lead with an account remains. Unknown emails are ignored.

Response 200

Project updated. Field availability depends on your organization's configuration — see the schema.

FieldTypeDescription
dataobjectA project read back via GET /v1/projects/{project_id}. Same fields as the create response, except that some sensitive fields may be omitted depending on your organization's configuration. An API key is an organization credential, so this can return a project nobody using that key created.
data.billing_codestring or nullReturns null unless your organization is configured to return billing codes.
data.billing_office_namestring or nullThe resolved billing office name, or null if none. Returns null unless your organization is configured to return billing offices.
data.created_atstring (date-time) or null—
data.idstring (uuid)—
data.invited_lead_emailsarray of stringActive pending LEAD project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.invited_member_emailsarray of stringActive pending MEMBER project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.is_archivedbooleanWhether the project is archived. Set with POST /v1/projects/{project_id}/archive and POST /v1/projects/{project_id}/unarchive.
data.lead_emailsarray of stringThe lead users applied. Present only when your organization is configured to return participant emails — omitted entirely otherwise, so an absent key is indistinguishable from an empty list. Sorted alphabetically: the create echoes emails in request order, which isn't recoverable on a re-read. Pending lead invitations are listed separately in invited_lead_emails.
data.member_emailsarray of stringThe member users applied. Same participant-email gate and ordering as lead_emails.
data.namestring—
data.sectorstring or null—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
404Not Found — no project with this id in your organization. Deliberately indistinguishable from a project that belongs to another organization.
429Too Many Requests — rate limit exceeded.
503Service Unavailable — the write surface is not configured for this deployment.

Archive a project

POST /v1/projects/{project_id}/archive · write access

Archives a project your organization owns. Idempotent: archiving an already-archived project returns 200 and changes nothing. Requires the organization to be enabled for the write surface.

The response echoes the project's state after the archive, gated per field exactly like GET /v1/projects/{project_id}.

Path parameters

ParameterTypeDescription
project_id requiredstring (uuid)—

Response 200

Project archived. Field availability depends on your organization's configuration — see the schema.

FieldTypeDescription
dataobjectA project read back via GET /v1/projects/{project_id}. Same fields as the create response, except that some sensitive fields may be omitted depending on your organization's configuration. An API key is an organization credential, so this can return a project nobody using that key created.
data.billing_codestring or nullReturns null unless your organization is configured to return billing codes.
data.billing_office_namestring or nullThe resolved billing office name, or null if none. Returns null unless your organization is configured to return billing offices.
data.created_atstring (date-time) or null—
data.idstring (uuid)—
data.invited_lead_emailsarray of stringActive pending LEAD project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.invited_member_emailsarray of stringActive pending MEMBER project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.is_archivedbooleanWhether the project is archived. Set with POST /v1/projects/{project_id}/archive and POST /v1/projects/{project_id}/unarchive.
data.lead_emailsarray of stringThe lead users applied. Present only when your organization is configured to return participant emails — omitted entirely otherwise, so an absent key is indistinguishable from an empty list. Sorted alphabetically: the create echoes emails in request order, which isn't recoverable on a re-read. Pending lead invitations are listed separately in invited_lead_emails.
data.member_emailsarray of stringThe member users applied. Same participant-email gate and ordering as lead_emails.
data.namestring—
data.sectorstring or null—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
404Not Found — no project with this id in your organization. Deliberately indistinguishable from a project that belongs to another organization.
429Too Many Requests — rate limit exceeded.
503Service Unavailable — the write surface is not configured for this deployment.

Unarchive a project

POST /v1/projects/{project_id}/unarchive · write access

Unarchives a project your organization owns. Idempotent: unarchiving a project that is not archived returns 200 and changes nothing. Requires the organization to be enabled for the write surface.

The response echoes the project's state after the unarchive, gated per field exactly like GET /v1/projects/{project_id}.

Path parameters

ParameterTypeDescription
project_id requiredstring (uuid)—

Response 200

Project unarchived. Field availability depends on your organization's configuration — see the schema.

FieldTypeDescription
dataobjectA project read back via GET /v1/projects/{project_id}. Same fields as the create response, except that some sensitive fields may be omitted depending on your organization's configuration. An API key is an organization credential, so this can return a project nobody using that key created.
data.billing_codestring or nullReturns null unless your organization is configured to return billing codes.
data.billing_office_namestring or nullThe resolved billing office name, or null if none. Returns null unless your organization is configured to return billing offices.
data.created_atstring (date-time) or null—
data.idstring (uuid)—
data.invited_lead_emailsarray of stringActive pending LEAD project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.invited_member_emailsarray of stringActive pending MEMBER project invitations. Present only when participant emails are permitted, sorted alphabetically. API-created invitations create no account and send no invitation email; they expire after one year.
data.is_archivedbooleanWhether the project is archived. Set with POST /v1/projects/{project_id}/archive and POST /v1/projects/{project_id}/unarchive.
data.lead_emailsarray of stringThe lead users applied. Present only when your organization is configured to return participant emails — omitted entirely otherwise, so an absent key is indistinguishable from an empty list. Sorted alphabetically: the create echoes emails in request order, which isn't recoverable on a re-read. Pending lead invitations are listed separately in invited_lead_emails.
data.member_emailsarray of stringThe member users applied. Same participant-email gate and ordering as lead_emails.
data.namestring—
data.sectorstring or null—

Errors

StatusMeaning
400Bad Request — missing or invalid parameters.
401Unauthorized — missing or invalid API key.
403Forbidden. Three distinct causes, told apart by code: rest_not_enabled — the REST API is not switched on for this organization; write_not_enabled — it is, but the write surface is not (write endpoints only); forbidden — the key is valid but the request came from an IP outside its allowlist. None is fixed by reissuing the key.
404Not Found — no project with this id in your organization. Deliberately indistinguishable from a project that belongs to another organization.
429Too Many Requests — rate limit exceeded.
503Service Unavailable — the write surface is not configured for this deployment.