Applications
GET /api/v1/applications returns the applications in your workspace a page at a time, and GET /api/v1/applications/{id} returns one, with every parameter and field.
Overview
GET /api/v1/applications lists the applications in your workspace, and GET /api/v1/applications/{id} returns one of them. You get the applications you can see in the app. A key limited to some teams gets only the applications in those teams, never one someone shared with you from another team. A key with access to every team also gets the applications shared with you.
Both need the Read candidate data permission, which the API names candidates:read. A key can hold it only while a workspace admin has Candidate data in the API turned on in Settings → API. Turning it off takes the permission from every key, and the next call answers 403 with the code insufficient-scope. The Read permission alone gets the same 403.
An application names its candidate by id and nothing else. No name, email, phone number, note, file or score comes back.
Authentication, Errors and Rate limits cover what every operation shares. This page covers the parameters and the fields.
Request
curl "https://attia.app/api/v1/applications?job_id=0192f3a1-6b02-7d4c-8e11-5a9c0b7d3e01" \ -H "Authorization: Bearer $ATTIA_API_KEY"All six query parameters are optional.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
limit | integer, 1 to 100 | 25 | How many applications to return |
cursor | string | none | The next_cursor of the previous page |
updated_since | RFC 3339 timestamp | none | Only applications updated at or after this time |
include_archived | true or false | false | Also return archived applications |
job_id | UUID | none | Only applications to this job |
current_status_id | UUID | none | Only applications in this status |
A parameter outside this table gets a 400 with the code invalid-request. So does a parameter sent twice, and a job_id or current_status_id that is not a UUID.
Response
The answer holds the applications in data, sorted by updated_at and then id, oldest change first. next_cursor is a string when more applications exist and null on the last page.
{ "data": [ { "id": "0192f4b7-2c10-7a3e-9d21-6e0f1a2b3c01", "team_id": "0192f39f-11aa-7c3e-b2d4-9e8f7a6b5c4d", "job_id": "0192f3a1-6b02-7d4c-8e11-5a9c0b7d3e01", "workspace_candidate_id": "0192f4b6-8e44-7f10-a3c2-1d2e3f4a5b01", "current_status_id": "0192f3a0-55c1-7b2d-8f90-0a1b2c3d4e05", "board_category": "active", "source_type": "career_page", "source_label": null, "source_url": null, "applied_at": "2026-10-02T08:15:30.482913Z", "withdrawn_at": null, "archived_at": null, "assigned_to_workspace_member_id": "0192f39e-0b7a-7c11-9e3f-2a4b6c8d0e12", "public_revision": 3, "updated_at": "2026-10-03T11:20:04.007311Z" } ], "next_cursor": null}| Field | Type | Meaning |
|---|---|---|
id | UUID | The application's id. It never changes |
team_id | UUID | The team the application sits in |
job_id | UUID | The job applied to. GET /api/v1/jobs/{id} returns it |
workspace_candidate_id | UUID | The candidate. A person with three applications has the same id on all three |
current_status_id | UUID | The application's status. Each team has its own statuses |
board_category | enum | backlog, new, active, completed or closed. The status category, which follows from the status |
source_type | enum | career_page, linkedin, referral, manual, job_board, outreach, imported or other |
source_label | string or null | The source's name, when one was recorded |
source_url | string or null | The source's address, when one was recorded |
applied_at | timestamp or null | When the candidate applied |
withdrawn_at | timestamp or null | When the candidate withdrew |
archived_at | timestamp or null | When the application was archived |
assigned_to_workspace_member_id | UUID or null | The workspace member assigned to the application |
public_revision | integer | Goes up by one on each change to a field above, and on some changes the API does not show. Order changes to one application by it |
updated_at | timestamp | When the application last changed |
updated_at and public_revision can both move when nothing above changed. updated_at moves on every change to the application. public_revision moves when a field above changes, or one of a few fields the API does not return, such as the review score.
An enum can gain a value in version 1, so handle one you do not know. Timestamps, nulls and enums describes each format.
Page and poll
Paging and polling work as they do for jobs. Send next_cursor back as cursor until it is null, and poll with updated_since set five minutes before the largest updated_at you have stored. Jobs walks through both, and Pagination explains the sort order. The filters stay the same on every page, so send job_id and current_status_id again with each cursor.
The list leaves archived applications out unless you send include_archived=true. When you keep a copy, poll with it, or an application someone archives drops out of your answers and your copy never learns why.
Get one application
GET /api/v1/applications/{id} returns the application with that id. It takes no query parameters, so one gets a 400 with the code invalid-request.
curl "https://attia.app/api/v1/applications/0192f4b7-2c10-7a3e-9d21-6e0f1a2b3c01" \ -H "Authorization: Bearer $ATTIA_API_KEY"The answer holds the application in data, with the same fields as an application in the list. An archived application comes back too, with a time in archived_at.
A 404 with the code not-found means no application your key can see has that id. It may not exist, or be deleted, or belong to another workspace. It may sit in a team the list leaves out for you, or its candidate may have been erased or hidden from the workspace. Every one of these gets the same answer, so a 404 never tells you whether the application exists. An id that is not a UUID gets the same 404.