People

List notes across the agency

Changed on

Use this endpoint to keep an external system in step with the notes your recruiters record against people in Atlas - for example, to report on business-development activity such as a logged BD call.

This is the agency-wide counterpart to POST /api/v1/people/notes/lookup. The lookup endpoint requires you to name the people you are interested in (up to 100 per request), which means an account with tens of thousands of contacts would need hundreds of calls per polling cycle. This endpoint instead returns every note in your agency, newest change first, so you can sync on a timestamp cursor and never send a person ID at all.

Keeping in sync (recommended pattern):

  1. Backfill once. Page through the endpoint using createdAfter / createdBefore to walk your history in windows (for example a month at a time). Both bounds are inclusive and accept either a full ISO 8601 datetime or a date-only YYYY-MM-DD value.
  2. Record your cursor. Keep the highest updatedAt you have fully processed. Seed it with the time your backfill started, not the time it finished, so a note edited while the backfill was running is picked up by your first poll instead of being skipped.
  3. Poll a closed window. On each subsequent poll (every few minutes is fine) send updatedAfter set to your stored cursor minus a small overlap - a minute or two, to absorb clock skew and rows committed mid-request - together with updatedBefore set to the current time. Pinning the upper bound matters: paging is offset-based over an updatedAt descending sort, so without it a note edited while you are partway through the pages jumps to the top of the result set and pushes a row you have not read yet off the end of the page you are about to request. The overlap means you will occasionally re-receive a note you already have, so de-duplicate on id and treat a repeat as an update rather than a new note.
  4. Drain every page of that window. pageSize caps at 100, so any interval with more changes than that spans several pages - keep requesting page 2, 3, ... until you have read totalPages. Because the sort is newest change first, the later pages hold the older changes in the window: stopping after page 1 and advancing the cursor puts them permanently behind it.
  5. Advance the cursor to the updatedBefore you pinned in step 3, and only once every page of the window has been processed. If a poll fails part-way, leave the cursor untouched and re-run the same window - de-duplicating on id makes the replay harmless.

A note that is edited gets a fresh updatedAt, so edits arrive through the same cursor as new notes. Notes that have not changed are not returned.

Picking up deletions: Deleted notes are retained as tombstones. Pass includeDeleted=true and a soft-deleted note comes back with its deletedAt populated (it is null for live notes); deleting a note also bumps its updatedAt, so deletions flow through your updatedAfter cursor alongside creations and edits. Without the flag, only live notes are returned.

Optional narrowing:

  • type - comma-separated note types. manual is a note typed by a user, phone_call is a note logged against a call (these also carry a phoneCallOutcome), and integration is a note written by an inbound integration rather than a person.
  • personId - restrict to a single person, for drill-down. Leave it off for the sync path.
  • projectId - only the notes scoped to one project, i.e. the notes written on that project's candidates. One call returns the notes for every candidate in the project.
  • candidateId - only the notes on one candidate in a project (the candidate's person, scoped to the candidate's project). Returns an empty page when the candidate does not exist in your agency, including a candidate removed from its project - use personId with projectId to read those notes.

All filters combine. Person-level notes (projectId null) never match projectId or candidateId.

What you get back: A paginated list of notes sorted by updatedAt descending, then by id. Each note includes its text, type, the person it belongs to, the project it is scoped to (projectId - set when the note was added against the person's candidacy on a specific project, null for a person-level note), the author (id, name, email), phoneCallOutcome where the note came from a logged call, and its timestamps.

Results are always scoped to your agency, and an empty result set is a successful response with an empty data array, not an error.

get/api/v1/people/notes

Request

Query parameters

personIdstring uuid

Drill down to the notes of a single person. Omit to list notes across the whole agency (the incremental-sync path).

Example:550e8400-e29b-41d4-a716-446655440000
projectIdstring uuid

Only notes scoped to this project (the notes written on candidates in it). Omit to include person-level notes and notes on every project.

Example:9f8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
candidateIdstring uuid

Only the notes on one candidate: the candidate's person, scoped to the candidate's project. Returns an empty page when no candidate with this ID exists in your agency, including a candidate removed from its project (use personId with projectId for those notes).

Example:7c9e6679-7425-40de-944b-e07fc1f90ae7
typestring

Comma-separated note types to include. One or more of: phone_call, manual, integration. Defaults to all types.

Example:phone_call
string
OR
string date-time
string
OR
string date-time
string
OR
string date-time
string
OR
string date-time
includeDeleted'true' | 'false'

Include soft-deleted notes as tombstones (with a populated deletedAt). Defaults to false. Pair with updatedAfter to incrementally sync deletions.

Example:false
pageinteger

Page number (1-indexed)

Example:1
pageSizeinteger

Items per page (max 100)

Example:25
perPageinteger

Alias for pageSize

Response

Paginated agency-wide notes, sorted by updatedAt descending

status'ok' required

Example response

{
  "data": [
    {
      "text": "BD call - happy for us to send two profiles this week.",
      "type": "phone_call",
      "createdBy": {
        "name": "Sarah Johnson",
        "email": "sarah@agency.com"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 100,
    "total": 1,
    "totalPages": 1
  }
}

Changes

    • warning

      added the new undefined enum value to the // response property for the response status

    • info

      added the new optional query request parameter candidateId

    • info

      added the new optional query request parameter projectId