Invite a candidate
curl --request POST \
--url https://app.dotportion.com/api/v1/assessments/{id}/invitations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({candidate_email: 'ada@example.com', candidate_name: 'Ada Lovelace'})
};
fetch('https://app.dotportion.com/api/v1/assessments/{id}/invitations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.dotportion.com/api/v1/assessments/{id}/invitations"
payload = {
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"id": "00000000-0000-4000-8000-0000000000a2",
"assessment_id": "00000000-0000-4000-8000-000000000011",
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace",
"status": "sent",
"recruiter_decision": "none",
"sent_at": "2026-01-15T12:00:00.000Z",
"opened_at": null,
"started_at": null,
"submitted_at": null,
"expires_at": "2026-01-22T12:00:00.000Z",
"created_at": "2026-01-15T12:00:00.000Z",
"access_token": "sandbox-token-not-redeemable",
"invitation_url": "http://localhost:3000/assess/sandbox-token-not-redeemable"
},
"meta": {
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request",
"details": {
"formErrors": [],
"fieldErrors": {
"name": [
"String must contain at least 1 character(s)"
]
}
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or revoked API key",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "PLAN_LIMIT_REACHED",
"message": "Sending 1 invitation(s) would exceed your plan limit for this period. Upgrade to send more.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key lacks the required scope: write.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for write requests (200/hour). Retry after 2026-01-15T13:00:00.000Z.",
"details": {
"limit": 200,
"remaining": 0,
"reset": 1768482000
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}Invitations
Invite a candidate
Invites one candidate and emails them. One per call on purpose: a partial failure across a batch would leave you unsure who was invited.
access_token and invitation_url are returned HERE AND ONLY HERE. Store the URL if you want to re-send it yourself; list responses omit the token, because a leaked list would otherwise hand over every candidate session.
Quota is consumed on invitation send, not on assessment create — so this is the endpoint that can answer 402.
POST
/
api
/
v1
/
assessments
/
{id}
/
invitations
Invite a candidate
curl --request POST \
--url https://app.dotportion.com/api/v1/assessments/{id}/invitations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({candidate_email: 'ada@example.com', candidate_name: 'Ada Lovelace'})
};
fetch('https://app.dotportion.com/api/v1/assessments/{id}/invitations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.dotportion.com/api/v1/assessments/{id}/invitations"
payload = {
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"id": "00000000-0000-4000-8000-0000000000a2",
"assessment_id": "00000000-0000-4000-8000-000000000011",
"candidate_email": "ada@example.com",
"candidate_name": "Ada Lovelace",
"status": "sent",
"recruiter_decision": "none",
"sent_at": "2026-01-15T12:00:00.000Z",
"opened_at": null,
"started_at": null,
"submitted_at": null,
"expires_at": "2026-01-22T12:00:00.000Z",
"created_at": "2026-01-15T12:00:00.000Z",
"access_token": "sandbox-token-not-redeemable",
"invitation_url": "http://localhost:3000/assess/sandbox-token-not-redeemable"
},
"meta": {
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request",
"details": {
"formErrors": [],
"fieldErrors": {
"name": [
"String must contain at least 1 character(s)"
]
}
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or revoked API key",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "PLAN_LIMIT_REACHED",
"message": "Sending 1 invitation(s) would exceed your plan limit for this period. Upgrade to send more.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key lacks the required scope: write.",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for write requests (200/hour). Retry after 2026-01-15T13:00:00.000Z.",
"details": {
"limit": 200,
"remaining": 0,
"reset": 1768482000
},
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"request_id": "req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c"
}
}Authorizations
Your API key, live (sk_live_…) or test (sk_test_…). Create one in Settings → API keys. Send it as Authorization: Bearer <key> — never in a query string.
Path Parameters
The assessment id.
Body
application/json
Was this page helpful?