Apricot API Endpoints (v1.0)

Download OpenAPI specification:

A list of selected endpoints for Apricot API, to be used by the Bonterra API

Apricot Health Check

Endpoint to check if the Apricot API is up

Checks whether the Apricot service is available. There is currently no scenario in which this endpoint returns a 2XX response - a healthy service responds with a 418 status and an empty body.

Responses

Apricot Users

Get list of users

Retrieves a list of all users in your Apricot instance. Pagination (page[size]) is required; filtering and sorting are optional.

Common use cases:

  • List all users for administrative purposes
  • Export user directory
  • Find users by name or email using filters
  • Sync user list with external systems
Authorizations:
authorizer-lambdaapi_key
query Parameters
sort
string
Examples:
  • sort=-mod_time - Sort by last-modified time, most recently modified first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/users?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Create a new user

Creates a new user account in Apricot. Requires username, first name, last name, and user type.

Common use cases:

  • Add new team members programmatically
  • Bulk import users from external systems
  • Automated user provisioning

Important notes:

  • Username must be unique (typically an email address)
  • Password is optional. If omitted, a random placeholder value is auto-generated; complexity requirements are only checked when a password value is supplied.
  • User type determines permissions
  • Only an Administrator (or Super User) can create another Admin user; otherwise the request fails with a 400
  • Org-level seat count limits (Administrator/Standard) are enforced before creation; exceeding them fails with a 400
  • When the organization has site_admins_enabled and the new user is not an Admin, a non-empty site_links array is required or the request fails with a 400
Authorizations:
authorizer-lambdaapi_key
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required
type
required
string

Resource type, must be 'users'

object

User attributes

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

Content type
{
  • "type": "users",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get your own user information

What it does: Retrieves the profile information for the currently authenticated user based on the access token. This is a self-service endpoint that returns details about "you" (the token owner) without needing to know your user ID.

When to use it:

  • Get your own user profile on login
  • Display current user information in UI
  • Verify authentication and user identity
  • Check your own permissions and roles
  • Build "My Profile" or "Account Settings" pages
  • Validate which Apricot instance you're connected to

What you'll need:

  • Valid access token (identifies you automatically)
  • No user ID required - uses token to determine identity

What you'll get back: Your complete user profile including:

  • id - Your unique user ID
  • org_id - Your organization ID
  • username - Your login username
  • user_type - Your role/permissions level
  • name_first, name_middle, name_last - Your full name
  • active - Whether your account is active
  • Custom user attributes

Use cases:

  • "Who am I?" - Identity verification after authentication
  • Displaying logged-in user name in application header
  • Checking if you have required permissions before actions
  • Personalizing the user experience based on your profile

vs. GET /users/{id}:

  • This endpoint: Gets YOUR info (token-based, no ID needed)
  • GET /users/{id}: Gets ANY user's info (requires user ID and admin permissions)

Important notes:

  • Returns 404 if the token owner's own user record can't be resolved (same lookup path as GET /users/{id})
Authorizations:
authorizer-lambdaapi_key
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/users/info" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get a specific User

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Update a user

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required
type
required
string

Resource type, must be 'users'

object

User attributes

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

Content type
{
  • "type": "users",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get Organizations by Bonterra Auth User ID

Retrieves all Apricot organizations that a Bonterra Auth user is linked to. This endpoint is used for authorization purposes by Account and Impact Hub services.

Purpose:

  • Query linked Apricot users for a given Bonterra Auth user ID
  • Retrieve organization membership and user details
  • Support authorization decisions across services

Personas:

  • Account service (Authorization Code Grant & Client Credentials Grant)
  • Impact Hub service (Authorization Code Grant & Client Credentials Grant)

Important notes:

  • Returns empty array if user has no organizations (still 200 status)
  • Returns 404 only when the Bonterra Auth user ID doesn't exist
  • Returns 400 if bonterra_auth_id is missing
  • User can belong to multiple organizations with different roles
  • Only organizations that are active (org_active) and have a real org_id (> 0) are ever returned; inactive organizations and the synthetic org_id=0 super-user row are always excluded from orgs
  • If the caller (the authenticated requester, not the target bonterra_auth_id user) belongs to a real organization, the results are scoped to that organization only - the caller cannot see the target user's memberships in other organizations
  • is_super_user being true does not imply an empty orgs array - a super user with real active organization memberships will still have them listed
Authorizations:
authorizer-lambdaapi_key
path Parameters
bonterra_auth_id
required
string
Example: 37f9a9b0-76ac-4d13-a3b4-8eb9873a24f5

Bonterra Auth ID (GUID, e.g., 37f9a9b0-76ac-4d13-a3b4-8eb9873a24f5)

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
required
Array of objects (ApricotUserOrganizationMembership)

Array of organization memberships. Each item represents the user's relationship with an organization. Empty array if user has no organizations.

bonterra_auth_id
required
string

The Bonterra Auth user ID

is_super_user
boolean

Indicates the caller is a super user. Only present (and only ever true) when BOTH the calling Bonterra Auth user is a super user AND a synthetic org_id=0 row exists in the underlying org list for that user; a super user without that synthetic row will not have this field in the response.

Response samples

Content type
application/json
Example
{
  • "bonterra_auth_id": "37f9a9b0-76ac-4d13-a3b4-8eb9873a24f5",
  • "orgs": [
    ]
}

Get users who have access to a program

What it does: Retrieves a list of all users who have been granted access to a specific program. This helps you manage program-level permissions and understand who can view/edit data within a program.

When to use it:

  • Audit which users have access to a program
  • Manage program-level user permissions
  • Verify user access before assigning work
  • Build user access reports and matrices
  • Onboard new staff to see team composition
  • Identify users to notify about program changes
  • Export user lists for program documentation

What you'll need:

  • Program ID (get from GET /apricot/programs)
  • Pagination parameters (page[size] is required); sorting and filtering are optional
  • Access token with apricot.read scope

What you'll get back: Array of user objects with access to this program, each containing:

  • User ID, username (used for login; there is no separate email field)
  • User's full name (first and last)
  • User's user_type (e.g. Administrator, Standard User, Guest), which determines permissions level - there is no separate scalar role field
  • Active status
  • Last login information
  • User metadata

Inactive users are always excluded from the results; there is no way to opt in to seeing them.

Common use cases:

  • Access Control: "Who can see this program's data?"
  • Team Management: List all case workers in the Housing program
  • Reporting: Generate staff rosters per program
  • Troubleshooting: Verify why a user can/cannot access program data

Note: This returns users with explicit program access. System administrators may have access to all programs without being explicitly listed.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-mod_time - Sort by last-modified time, most recently modified first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/programs/2/users?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Apricot Programs

Get list of programs

Retrieves a list of all programs in your Apricot instance. Programs are used to organize records and users by service type or department.

Common use cases:

  • List all programs for administrative purposes
  • Get program IDs for filtering records
  • Display programs in UI dropdowns
  • Export program configurations
Authorizations:
authorizer-lambdaapi_key
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/programs?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Create a new program

What it does: Creates a new program in your Apricot instance. Programs are the top-level organizational structure used to group related forms, records, and users. Each program typically represents a distinct service area, grant-funded initiative, or department.

When to use it:

  • Set up a new service program or initiative
  • Create a program for a new grant or funding source
  • Establish a departmental data collection area
  • Organize data for different service populations
  • Separate data by geographic region or location
  • Create programs for different fiscal years

What you'll need:

  • Program name (required)
  • Site ID (required) - the program must be linked to an existing site
  • Program description (optional but recommended)
  • Access token with apricot.create scope
  • Organization Admin or Site Admin permissions

What you'll get back: The newly created program object including:

  • Unique program ID (use this for record creation)
  • All provided attributes
  • Creation timestamp
  • Default settings for new programs

Important notes:

  • The API does not enforce unique program names
  • New programs are always created as active; the active field is ignored/overridden on creation
  • Requires Admin or Site Admin permissions; other users receive a 403 Forbidden
  • Programs cannot be deleted via API (deactivate instead)

Common program types:

  • Service delivery programs (e.g., "Housing Assistance", "Job Training")
  • Grant-funded initiatives (e.g., "2025 SAMHSA Grant")
  • Administrative divisions (e.g., "Northern Region", "Youth Services")
  • Time-based programs (e.g., "FY2025 Emergency Relief")
Authorizations:
authorizer-lambdaapi_key
Request Body schema:
required

Program data for the new program

required
object

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

Content type
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get a specific program by ID

What it does: Retrieves detailed information about a specific program. Programs in Apricot are organizational containers that group related forms, records, and users together, representing distinct service areas or organizational divisions.

When to use it:

  • Get details about a specific program
  • Check if a program is active before creating records
  • Retrieve program metadata and configuration
  • Verify program names and descriptions
  • Build program-specific dashboards or reports
  • Validate program IDs before assigning records

What you'll need:

  • Program ID (get from GET /apricot/programs)
  • Access token with apricot.read scope

What you'll get back: Complete program object including:

  • id - Unique program identifier
  • name - Program name (e.g., "Youth Services", "Housing Assistance")
  • description - Program description
  • active - Whether the program is currently active
  • mod_time / mod_user - Last modification timestamp and user
  • Custom program-level fields and metadata

Program hierarchy: Programs can contain multiple forms, and each form can contain multiple records. Understanding the program structure is essential for proper data organization.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/programs/2" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Update a program

What it does: Updates an existing program's properties. You can modify the program name, description, active status, and other program-level attributes. This operation performs a partial update, so you only need to include the fields you want to change.

When to use it:

  • Change a program's name or description
  • Activate or deactivate a program
  • Update program settings or configuration
  • Modify program metadata
  • Update program associations (forms, users)
  • Correct program information

What you'll need:

  • Program ID
  • Fields you want to update (only send fields that should change)
  • Access token with apricot.update scope

What you'll get back: The complete updated program object with all fields, including:

  • All attributes (both changed and unchanged)
  • Updated timestamp showing when the modification occurred
  • Confirmation of the new values

Important notes:

  • This is a partial update - only include fields you want to change
  • The program to update is determined solely by the id URL path parameter; any id/type values in the request body are ignored
  • Changing a program to inactive may affect record creation
  • Some fields may be read-only depending on your permissions

Common update scenarios:

  • Deactivating a program at the end of a grant period
  • Renaming a program for clarity
  • Updating program descriptions as services evolve
  • Modifying program settings for new requirements
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
Request Body schema:
required

Program fields to update (partial update supported)

required
object

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

Content type
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get program configuration for Connect intake

What it does: Retrieves the same program object as GET /apricot/programs/{id}, scoped to the caller's intake-specific permissions rather than their general program-access permissions. It is intended for Bonterra Connect intake and referral workflows that need to look up an Apricot program's details before creating a record in it.

When to use it:

  • Look up an Apricot program's details as part of a Connect intake/referral workflow
  • Verify a program exists and check its active status before creating a referral record
  • Retrieve program details when the caller's access token is scoped for intake rather than general program access

What you'll need:

  • Program ID from Apricot
  • Access token scoped for intake access

What you'll get back: The identical program object returned by GET /apricot/programs/{id} (same id, name, description, active status, referral settings, and other program attributes). There is no separate "intake format" - the response shape is identical.

Integration scenario:

  1. Connect receives a referral or intake request
  2. Connect queries this endpoint to get Apricot program details
  3. User selects which Apricot program to refer client to
  4. Connect creates a record in Apricot using the program configuration

vs. GET /programs/{id}:

  • This endpoint: Same response shape, but authorizes against intake-scoped permissions instead of general program-access permissions, and does not record the "program viewed" audit event/record action that GET /programs/{id} records
  • GET /programs/{id}: Standard program details, authorized against general program-access permissions
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/programs/2/intake" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get users who have access to a program

What it does: Retrieves a list of all users who have been granted access to a specific program. This helps you manage program-level permissions and understand who can view/edit data within a program.

When to use it:

  • Audit which users have access to a program
  • Manage program-level user permissions
  • Verify user access before assigning work
  • Build user access reports and matrices
  • Onboard new staff to see team composition
  • Identify users to notify about program changes
  • Export user lists for program documentation

What you'll need:

  • Program ID (get from GET /apricot/programs)
  • Pagination parameters (page[size] is required); sorting and filtering are optional
  • Access token with apricot.read scope

What you'll get back: Array of user objects with access to this program, each containing:

  • User ID, username (used for login; there is no separate email field)
  • User's full name (first and last)
  • User's user_type (e.g. Administrator, Standard User, Guest), which determines permissions level - there is no separate scalar role field
  • Active status
  • Last login information
  • User metadata

Inactive users are always excluded from the results; there is no way to opt in to seeing them.

Common use cases:

  • Access Control: "Who can see this program's data?"
  • Team Management: List all case workers in the Housing program
  • Reporting: Generate staff rosters per program
  • Troubleshooting: Verify why a user can/cannot access program data

Note: This returns users with explicit program access. System administrators may have access to all programs without being explicitly listed.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-mod_time - Sort by last-modified time, most recently modified first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotUser)

Array of user objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/programs/2/users?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Copy/clone a program

What it does: Creates a copy (clone) of an existing program's attributes and permission sets. This is useful when you need to create a new program with similar settings to an existing one, saving time compared to manually recreating everything from scratch.

When to use it:

  • Create a new program based on an existing template
  • Replicate a program structure for a new grant year or funding cycle
  • Establish similar programs for different geographic regions
  • Set up pilot programs based on existing successful programs
  • Create backup copies of program configurations
  • Standardize program structure across your organization

What you'll need:

  • Source program ID (the program to copy from)
  • Optional: New program name and description (if name is omitted, the copy inherits the source program's exact name)
  • Access token with apricot.create scope
  • Organization Admin or Site Admin permissions

What gets copied:

  • Program-level attributes and settings (name, description, contact info, referral settings, etc.), overridden by anything provided in the request body
  • Permission sets (and their assigned users) - always copied, this cannot be opted out of

What does NOT get copied:

  • Forms - Programs have no form association to copy
  • Actual records (data)
  • Record-level attachments or files
  • Historical data or audit logs

What you'll get back: The newly created program object including:

  • New unique program ID
  • All copied attributes and settings
  • Creation timestamp
  • Confirmation of successful duplication

Important notes:

  • The API does not enforce unique program names; omitting name silently produces a duplicate name
  • Records from the source program are NOT copied
  • Forms are not copied or associated in any way; Programs have no form association
  • Permission sets are always copied along with their members; you cannot opt out

Common scenarios:

  • "Youth Services 2024" → "Youth Services 2025"
  • "Regional Program - North" → "Regional Program - South"
  • "Pilot Housing Assistance" → "Housing Assistance Program"
  • Template programs for standardizing across departments
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
Request Body schema: application/json
required

New program details for the copy

required
object

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

Content type
application/json
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get a list of all Apricot Programs for Connect Intake

Get a list of all Apricot Programs the caller has intake-scoped access to.

Unlike GET /apricot/programs, which returns 200 with an empty array when there are no matching programs, this endpoint returns 404 when there are no intake-eligible programs.

Authorizations:
authorizer-lambdaapi_key
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Apricot Records

Get records by form ID

Retrieves all records for a specific form. Use this endpoint to fetch a list of records that belong to a particular form. Results can be paginated.

Common use cases:

  • List all client intake records
  • Export form data for reporting
  • Sync records with external systems

Filtering: filter[field_<id>]=value performs a SQL LIKE match on that field, where <id> is the field's numeric ID (e.g. field_2002, discoverable via GET /apricot/forms/{id}/fields) - not the field's label. The value is passed directly to LIKE, so it supports % as a wildcard (e.g. filter[field_2002]=John% matches values starting with "John"); a value with no % matches only that exact string.

PII filtering and field metadata:

  • pii_filter=true removes PII fields (name, address, email, phone, SSN, photo/signature, etc.) from every record in the response.
  • include_field_metadata=true adds a meta.field_metadata map (keyed by field_<id>, each entry containing id, label, field_type_id, field_type_name) describing the fields present in the response.
Authorizations:
authorizer-lambdaapi_key
query Parameters
form_id
required
integer <int32>
Example:

The ID of the form to query records from

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

include
string
Example: include=field_1001,field_2_first

Comma-separated list of field names to include in the response (e.g. field_1001,field_2_first). Only the listed fields (plus system fields) are returned in each record's attributes. If omitted, all active fields for the form are returned.

pii_filter
string
Enum: "true" "false"
Example: pii_filter=true

When true, strips fields whose field type is considered personally identifiable information (e.g. name, address, email, phone, SSN, photo/signature fields) from every record in the response.

include_field_metadata
string
Enum: "true" "false"
Example: include_field_metadata=true

When true, adds a meta.field_metadata map to the response, keyed by field_<id>, with each entry containing id, label, field_type_id, and field_type_name for that field. If pii_filter=true is also set, PII field types are omitted from this map.

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records?form_id=100&page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Create a new record

What it does: Creates a new record in the specified form. This endpoint supports three content types for different use cases, including the ability to create a record with file attachments in a single request.

When to use it:

  • Add a new client intake record
  • Create records programmatically from external systems
  • Bulk import data into Apricot
  • Create records with initial file attachments (using multipart/form-data)

What you'll need:

  • Form ID (required - get this from GET /apricot/forms)
  • Field values matching your form's field IDs (field_1001, field_1002, etc.)
  • For attachments: Files to upload (PDF, DOCX, images, etc.)
  • Access token with apricot.create scope

What you'll get back: Newly created record with ID and all field values including attachment metadata if files were uploaded

Three Content Type Options:

1. application/json (Most common) Use for creating records without files. Simple JSON body with field values.

2. application/vnd.api+json (JSON API format) Use if your application follows JSON API specification.

3. multipart/form-data (For records with attachments - Option 1) Use when creating a new record AND you have files ready to attach immediately. This is the "Option 1" approach for working with attachments.

Two Approaches for Attachments:

Option 1: Create Record with Attachments (this endpoint with multipart/form-data)

  • ✅ Atomic operation - record and files created together
  • ✅ Best for: New records with initial attachments
  • ✅ All data and files ready at once
  • Use multipart/form-data content type
  • See examples below

Option 2: Add Attachment to Existing Record

  • Use POST /apricot/records/{id}/attachment/{field_id}
  • Best for adding files to existing records
  • See that endpoint for details

Field Naming Patterns: Field IDs follow specific patterns based on field type:

  • Simple fields: field_1001, field_1002
  • Name fields: field_2_first, field_2_last
  • Address fields: field_99_line1, field_99_city, field_99_state, field_99_zip
  • Attachment fields: field_827, field_950 (uploaded as files in multipart/form-data)

Important notes:

  • The form_id parameter is required (query parameter or in attributes)
  • Field IDs are specific to each form's configuration
  • Use GET /apricot/forms/{id} to discover available fields and their IDs
  • Do NOT set Content-Type header manually when using multipart/form-data
  • Programs array is optional but recommended for multi-program forms

How the multipart/form-data body is actually parsed (important): The multipart parser does not look for a field named data - it has no special handling for any field name. For every non-file field it receives, it attempts JSON.parse(value); if that succeeds, the entire request body is replaced with the parsed result (not merged), regardless of what the field was named. Only if JSON.parse fails does the parser fall back to setting that one key (req.body[key] = value). This means:

  • You can technically name the JSON field anything (not just data) and it will work.
  • If you send more than one JSON-parseable field, only the last one processed wins - it silently overwrites the earlier one, with no error or warning.
  • For predictable results, send exactly one field containing the JSON record payload (conventionally named data) alongside your file fields.
Authorizations:
authorizer-lambdaapi_key
query Parameters
form_id
required
integer <int32>
Example:

The ID of the form to query records from

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required

For JSON requests (application/json or application/vnd.api+json): Send record data as JSON with type and attributes.

For multipart/form-data (creating record with attachments):

  • Add one field containing the JSON record data as a string (conventionally named data, though the parser does not check the field name)
  • Add file fields using field IDs (e.g., field_827, field_950)
  • Do NOT set Content-Type header - let client set it with boundary
  • Sending more than one JSON-parseable text field is unsupported: only the last one parsed will be used, silently overwriting the others
type
required
string

Resource type, must be 'records'

object (ApricotRecordBodyAttributes)

Attributes for creating or updating a record. Field names are dynamic based on form definition (e.g., field_1001, field_1002).

Array of objects (ApricotRecordBodyMetadata)

Optional metadata array to bypass UI validation requirements when creating/updating records

writeMode
string
Default: "default"
Enum: "default" "import"

Controls which internal write path processes the request. default is the normal create/update behavior; import is used for bulk/import-style writes.

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

Content type
Example
{
  • "type": "records",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get a specific record by ID

What it does: Retrieves a single record by its unique identifier. Returns the complete record including all field values, metadata, attachment information, timestamps, and relationships.

When to use it:

  • View detailed information for a specific client or record
  • Retrieve current record data before updating (recommended before PUT)
  • Check if a record exists and is accessible
  • Get attachment metadata for files associated with the record
  • Fetch related record information via links
  • Verify successful record creation

What you'll need:

  • Record ID (from POST /records response or GET /records list)
  • Access token with apricot.read scope

What you'll get back: A record collection object ({meta, data: [...]}) whose data array contains exactly one record, with:

  • id - The unique record identifier
  • type - Resource type (always "records")
  • attributes - All field values including:
    • form_id - The form this record belongs to
    • programs - Array of program IDs
    • Dynamic fields (field_1001, field_2_first, etc.)
    • Attachment metadata (filename, size, URL) for any file fields
    • creation_time, mod_time - Timestamps
  • links - Navigation URLs (self, form, related records)

Understanding the Response:

  • Field values match the field IDs from the form structure
  • Attachment fields contain file metadata objects with download URLs
  • Empty/null fields indicate no value was set
  • Timestamps are in ISO 8601 format

Important notes:

  • Returns 404 if the record doesn't exist
  • Returns 403 if the record exists but you don't have permission to view it
  • Use this before PUT to get current values and avoid overwriting data
  • Attachment fields show metadata but don't include file contents
  • Use GET /records/{id}/attachment/{field_id} to download actual files
  • Links section provides URLs to related resources
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
query Parameters
object
Example: filter[active]=1

Filter parameters to narrow down results. For this endpoint, only active is filterable; all other keys are ignored

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/123" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Update an existing record

What it does: Updates an existing record with new field values. You can update one field or multiple fields in a single request. Fields not included in the request remain unchanged.

When to use it:

  • Update client or participant information
  • Change record status, category, or other field values
  • Modify specific fields without affecting others
  • Correct or update existing data
  • Mark records as active/inactive

What you'll need:

  • Record ID (from GET /records or POST /records response)
  • Field values to update (only include fields you want to change)
  • Access token with apricot.update scope
  • Best practice: GET the record first to see current values

What you'll get back: Updated record object with all field values (not just the ones you updated)

Two Update Strategies:

Strategy 1: Partial Update (Recommended for most cases) Only send the fields you want to change. Other fields remain unchanged.

  • ✅ Simpler - less data to send
  • ✅ Faster - smaller payload
  • ⚠️ Risk: If another update happened between your GET and PUT, those changes are preserved

Strategy 2: Full Update (Safest for critical data) GET the record first, modify the fields you want, then PUT the complete attributes object.

  • ✅ Safer - you see exactly what you're changing
  • ✅ Prevents accidental overwrites
  • ⚠️ Slower - requires two API calls

Important notes:

  • Only fields in your request will be updated
  • Omitted fields keep their current values
  • Cannot update id, creation_time, or other system fields
  • Use GET first to retrieve current values (recommended)
  • Returns 404 if the record doesn't exist
  • Returns 400 if attributes.form_id doesn't match the record's current form - records cannot be moved between forms via this endpoint. form_id in the body must equal the record's existing form_id; a different value is rejected as an error, not treated as a move.
  • To update attachments, use POST /records/{id}/attachment/{field_id}

Common Issues:

"Required field missing" error:

  • Cause: Form has required fields that are empty
  • Solution: Include all required fields in your update, or use metadata.uiRequirementsToBypass

Accidentally clearing fields:

  • Problem: Sending null or empty string clears the field
  • Solution: Only include fields you explicitly want to change
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required

Update request body containing the fields to modify.

Partial update: Include only the fields you want to change Full update: Include all attributes from GET response with your changes

type
required
string

Resource type, must be 'records'

object (ApricotRecordBodyAttributes)

Attributes for creating or updating a record. Field names are dynamic based on form definition (e.g., field_1001, field_1002).

Array of objects (ApricotRecordBodyMetadata)

Optional metadata array to bypass UI validation requirements when creating/updating records

writeMode
string
Default: "default"
Enum: "default" "import"

Controls which internal write path processes the request. default is the normal create/update behavior; import is used for bulk/import-style writes.

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

Content type
Example
{
  • "type": "records",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get a record's attachment file

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
field_id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/octet-stream
string <binary>

Response samples

Content type
application/json
{
  • "users": [
    ]
}

Upload or update an attachment for a record

What it does: Uploads a file attachment to a specific field on an existing record. This is one of two ways to add attachments to records in Apricot.

When to use it:

  • Adding files to an existing record
  • Updating or replacing existing attachments
  • Uploading files after record creation
  • File-only operations without changing other record data

What you'll need:

  • Record ID (from a previously created record)
  • Field ID (the attachment field from your form - see "Finding Field IDs" below)
  • File to upload (PDF, DOCX, XLSX, PNG, JPG, etc.)
  • Access token with apricot.create or apricot.update scope

What you'll get back: Success response with attachment metadata including file ID, filename, size, and download URL

Two Approaches for Working with Attachments:

Option 1: Create Record with Attachments (multipart/form-data) Use this when creating a new record and you have files ready to attach immediately. See POST /apricot/records with Content-Type: multipart/form-data

  • ✅ Atomic operation - record and files created together
  • ✅ Best for: New records with initial attachments
  • ✅ All data and files ready at once

Option 2: Add Attachment to Existing Record (this endpoint) Use this endpoint when working with existing records.

  • ✅ Best for: Adding files to existing records
  • ✅ Updating or replacing attachments
  • ✅ File-only operations

Finding Field IDs: To find which field_id to use for attachments:

  1. Call GET /apricot/forms/{form_id} to get the form structure
  2. Look for fields where field_type_id indicates an attachment field
  3. Use the field's id value as the field_id parameter

Example: If you see a field with id: 827 and it's an attachment type, use field_id=827

IMPORTANT - multipart field name: The uploaded file must be sent in a form field named field_<field_id> (e.g. field_827 for field_id=827), NOT a field named "file". This is a common and easy mistake: if you send the field named "file" instead, the upload is silently ignored - the request still returns 200 OK with the record unchanged, and no file is attached.

Important notes:

  • Field ID must be a valid attachment field on the form
  • Supported file types are validated against the organization's allowed MIME type list
  • Do NOT manually set Content-Type header - let your HTTP client set it with the multipart boundary
  • Previous attachment on this field will be replaced with the new file

Common Issues:

Upload silently ignored (200 OK, record unchanged):

  • Cause: The file was sent under a field name other than field_<field_id> (e.g. "file"), or field_id does not correspond to an attachment field on the form
  • Solution: Use GET /apricot/forms/{form_id} to verify field exists and is an attachment type, and send the file under the field_<field_id> form field name

"Invalid file type" error:

  • Cause: The file's extension/MIME type is unrecognized, doesn't match the uploaded file's actual type, or is not in the organization's allowed MIME type list
  • Solution: Check file MIME type and ensure it's a supported format

Content-Type header issues:

  • Problem: Setting Content-Type manually breaks multipart upload
  • Solution: Let your HTTP client/library set Content-Type automatically with boundary
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
field_id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema: multipart/form-data
required

Upload a file using multipart/form-data. The file must be sent in a form field named field_<field_id> (e.g. "field_827" when field_id=827) - NOT a field named "file". Sending the field as "file" causes the upload to be silently ignored (200 OK, no file attached).

Important: Do not set Content-Type header manually - your HTTP client will set it correctly with the multipart boundary.

field_827
string <binary>

The file to upload (PDF, DOCX, PNG, JPG, etc.). The property name must match field_<field_id> for the field_id given in the path - "field_827" is shown here as an example for field_id=827.

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects

Array containing the single updated record, re-serialized with type "files"

Request samples

Content type
multipart/form-data
Example
{
  "field_827": "(binary file data)"
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get a record's linked records for a specific link field

What it does: Retrieves the full linked record objects for a given link field on a record. This is the endpoint that returns actual linked records; GET /apricot/records/{id}/links only returns per-field link-count summaries.

When to use it:

  • Fetch the full record data for records linked on a specific field
  • Navigate from a record to its linked records

What you'll need:

  • Record ID of the record whose links you want to retrieve
  • Field ID of the link field (see GET /apricot/records/{id}/links for the field IDs and counts available on a record)
  • Access token with apricot.read scope

What you'll get back: Collection of linked record objects (same shape as GET /apricot/records), with each linked record's data additionally including link_description, link_description_label, link_creation_time, and link_active for the specific link being returned.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
field_id
required
integer <int32>
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/vnd.api+json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/12345/links/field/827" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/vnd.api+json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get the IDs of a record's linked records for a specific link field (Admin only)

What it does: Retrieves just the record IDs linked to a record on a given link field, without loading the full linked record data or checking per-record permissions.

Restrictions: This endpoint is restricted to Admin users. Non-admin callers receive a 401, even with a valid access token and the right scope.

What you'll need:

  • Record ID of the record whose linked IDs you want to retrieve
  • Field ID of the link field
  • Access token for an Admin user, with apricot.read scope

What you'll get back: A count and a plain array of linked record IDs — not a JSON:API resource collection, unlike most other Apricot list endpoints.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
field_id
required
integer <int32>
query Parameters
object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/vnd.api+json
object (ApricotMeta)

Metadata about the response, including pagination information

data
Array of integers <int32> [ items <int32 > ]

Array of linked record IDs

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/12345/links/field/827/ids" \
  -H "Authorization: Bearer YOUR_ADMIN_ACCESS_TOKEN"

Response samples

Content type
application/vnd.api+json
{
  • "meta": {
    },
  • "data": [
    ]
}

Create an external referral for a record

What it does: Creates an external referral for a record, notifying an external recipient by email. The only fields accepted are email (the recipient's email address) and notes (free-text referral notes) - there is no organization name, service type, referral date, contact person/phone, or status field.

When to use it:

  • Refer a client to an external recipient by email
  • Send referral notes to someone outside your Apricot system

What you'll need:

  • Record ID of the client/participant being referred
  • email: the recipient's email address (required)
  • notes: the referral notes to send (required)
  • Access token with apricot.create scope
  • The record's form must have referrals enabled (can_send_referral); otherwise the request is rejected with a 403 (non-admin) or 400 (admin)

What you'll get back: The created referral resource (JSON:API type referrals), including status, the referring/receiving program IDs, and the notes submitted.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema: application/vnd.api+json
required

External referral details

type
string
object (ApricotExternalReferralBodyAttributes)

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotReferral)

Request samples

Content type
application/vnd.api+json
{
  • "type": "referrals",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get programs eligible to receive a new referral for a record

What it does: Returns the list of programs that this record could be referred to next - it is a candidate list for making a NEW referral, not a log of past referrals. The list excludes any program the record is already linked to, and is restricted to programs whose "referrals" setting allows receiving referrals.

When to use it:

  • Populate a picker of programs a record can be referred to
  • Check which programs are eligible to receive a referral for a record before submitting one

What you'll need:

  • Record ID of the client/participant
  • Access token with apricot.read scope

What you'll get back: A collection of bare program objects (id and name only) for the eligible programs. Results can be paginated, sorted, and filtered with the page, sort, and filter query parameters.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
query Parameters
key
string
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/12345/referrals/programs?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get record action history (audit log)

What it does: Retrieves the audit log / activity history of actions that have already been performed on records, such as when a record was Viewed, Created, Modified, Deleted, or Archived by Merge. Each entry is a historical audit event, not a configurable workflow action type - there is no concept of "available actions" or per-action permissions in this API.

When to use it:

  • Review the change history for records
  • Build activity feeds or audit trails
  • Track who created, viewed, or modified a record and when
  • Investigate when a record was archived or deleted

What you'll need:

  • Access token with apricot.read scope
  • Pagination parameters (page[size] is required); sort/filter are optional, to narrow down results

What you'll get back: Array of record action (audit log) entries, each containing:

  • The document/record the action was performed on
  • The action that was performed (e.g. "Record Created", "Record Modified", "Viewed", "Record Archived by Merge" - see the action field for the full set of real values)
  • The tier 1 record name and form name
  • The user who performed the action and when
Authorizations:
authorizer-lambdaapi_key
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=-creation_time - Sort by creation time, newest first

Sort order for results. Prefix with '-' for descending order

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecordAction)

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/actions?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Endpoint to search records by query

What it does: Search for records using a text query, optionally scoped to specific fields. This GET endpoint and POST /apricot/records/search call the same underlying search service and are functionally identical - this endpoint just accepts its parameters as query string parameters instead of a JSON body.

When to use it:

  • Find records by client name, email, or other text fields
  • Search across specific fields only (not all fields)
  • Filter records based on partial text matches

What you'll need:

  • Form ID (to specify which form's records to search)
  • Search query string (the text to search for)
  • Access token with apricot.read scope
  • Optional: fieldIdsToMatch to scope the search to specific fields

What you'll get back: Array of matching records with pagination metadata, similar to GET /records

Search Options:

form_id (required)

  • The form ID to search within
  • Get this from GET /apricot/forms

search (required)

  • The text query to search for
  • Searches across text fields in the form
  • Examples: "John Doe", "john@example.com", "Austin"

fieldIdsToMatch (optional)

  • Field IDs to search within, passed as repeated query params (e.g. fieldIdsToMatch=1001&fieldIdsToMatch=1002)
  • Default: Searches all searchable text fields
  • Use GET /apricot/forms/{id} to find field IDs

filter (optional)

  • Narrows search results further, using the same filter[key]=value syntax as other list endpoints
  • filter[id]=123 matches a specific record ID
  • filter[name]=Client matches against the form's designated record-name field
  • filter[field_<id>]=value matches against any other specific field

page (required)

  • page[size] is required and must be between 1 and 100; paginates search results

Important notes:

  • form_id and search are both required; omitting either returns a 400
  • A form_id that doesn't resolve to an existing form returns a 404
  • Searches text fields only (doesn't search dates, numbers, etc.)
  • Search terms are lowercased before matching, so matching is case-insensitive
  • An empty or whitespace-only search string tokenizes to nothing and returns zero records - it does NOT return all records in the form
  • Use fieldIdsToMatch to improve performance when you know which fields to search
  • There is no disableFuzzy, disablePrefix, disableWildcard, or wildcardPlacement option - this API does not expose configurable fuzzy/prefix/wildcard search behavior
Authorizations:
authorizer-lambdaapi_key
query Parameters
form_id
required
integer <int32>
Example:

The ID of the form to query records from

search
required
string
Example:

Search query string to match against record fields

fieldIdsToMatch
Array of integers
Example: fieldIdsToMatch=1001&fieldIdsToMatch=1002

Limit the search to specific field IDs, passed as repeated query params (e.g. fieldIdsToMatch=1001&fieldIdsToMatch=1002). If omitted, all searchable text fields on the form are searched.

object
Example: filter[active]=1

Filter parameters to narrow down results. Use field names as keys

required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/records/search?form_id=2&search=John%20Doe&page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Search records by query (JSON body)

What it does: Search for records using a text query, optionally scoped to specific fields. This POST endpoint and GET /apricot/records/search call the same underlying search service and are functionally identical - this endpoint just accepts its parameters as a JSON body instead of query string parameters.

When to use it:

  • Find records by client name, email, or other text fields
  • Search across specific fields only (not all fields)
  • Filter records based on partial text matches

What you'll need:

  • Form ID (to specify which form's records to search)
  • Search query string (the text to search for)
  • Access token with apricot.read scope
  • Optional: fieldIdsToMatch to scope the search to specific fields

What you'll get back: Array of matching records with pagination metadata, similar to GET /records

Search Options:

form_id (required)

  • The form ID to search within
  • Get this from GET /apricot/forms

search (required)

  • The text query to search for
  • Searches across text fields in the form
  • Examples: "John Doe", "john@example.com", "Austin"

fieldIdsToMatch (optional)

  • Array of specific field IDs to search within
  • Example: [1001, 1002] - only search in field_1001 and field_1002
  • Default: Searches all searchable text fields
  • Use GET /apricot/forms/{id} to find field IDs

filter (optional)

  • Narrows search results further, using the same filter[key]=value syntax as other list endpoints
  • filter: {id: 123} matches a specific record ID
  • filter: {name: "Client"} matches against the form's designated record-name field
  • filter: {field_<id>: value} matches against any other specific field

page (required)

  • page.size is required and must be between 1 and 100; paginates search results

Important notes:

  • form_id and search are both required; omitting either returns a 400
  • A form_id that doesn't resolve to an existing form returns a 404
  • Searches text fields only (doesn't search dates, numbers, etc.)
  • Search terms are lowercased before matching, so matching is case-insensitive
  • An empty or whitespace-only search string tokenizes to nothing and returns zero records - it does NOT return all records in the form
  • Use fieldIdsToMatch to improve performance when you know which fields to search
  • There is no disableFuzzy, disablePrefix, disableWildcard, or wildcardPlacement option - this API does not expose configurable fuzzy/prefix/wildcard search behavior
Authorizations:
authorizer-lambdaapi_key
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema: application/json
required

Search query

Required: form_id, search, and page (page.size) Optional: fieldIdsToMatch, to scope the search to specific fields; filter, to narrow results further

form_id
required
integer <int32>

ID of the form to search records in

search
required
string

Search query string to match against record fields

fieldIdsToMatch
Array of integers

Limit search to specific field IDs (if empty, searches all fields)

object

Narrows search results further. id matches a specific record ID (mapped internally to document_id); name matches against the form's designated record-name field; anything else is treated as a literal field_<id> key.

required
object

Paginates search results. size is required and must be between 1 and 100.

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

Content type
application/json
Example
{
  • "form_id": 2,
  • "search": "John Doe",
  • "page": {
    }
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Create a filtered subset of records

What it does: Retrieves a specific subset of records for a form, scoped to a caller-supplied list of record (document) IDs plus the desired field set. This lets you retrieve multiple specific records in a single request without having to make individual GET requests.

When to use it:

  • Retrieve multiple specific records in a single request (batch fetch)
  • Get records by a pre-filtered list of IDs from another system
  • Build custom record collections for reporting
  • Fetch records that match external system IDs
  • Optimize performance by reducing API calls
  • Create data exports for specific record sets

What you'll need:

  • Form ID to scope the query to
  • Array of desired fields to return
  • Array of record (document) IDs to include
  • Access token with apricot.read scope

What you'll get back: Collection of complete record objects matching the provided IDs, including:

  • All field values for each record
  • Record metadata (creation time, last updated, etc.)
  • Form association
  • Pagination metadata if result set is large

Performance tip: This is more efficient than making individual GET requests for each record when you need to fetch multiple specific records at once.

Authorizations:
authorizer-lambdaapi_key
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required

Form ID, desired fields, and array of record IDs to retrieve

form_id
required
integer <int32>

The tier 1 form id to query against

desired_fields
required
Array of strings

The fields that will be included in the result. For active queries, this should contain name and email fields, minimum. For archived queries, this set is ignored.

included_records
required
Array of integers <int32> [ items <int32 > ]

The document ids that should be included in the response

searching
string

If present, the desired_fields array will have sql like applied to each field with this string (active queries only)

object or null

Optional filters to narrow the queried records

object

Pagination for the results

object

Sort order for the results

pagination
boolean

Whether the results should be paginated

condensed_record_query
boolean
Default: false

If true, skips mapping the raw query results into fully-formed record objects

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

object

Envelope-level hypermedia links (in addition to the meta block)

Request samples

Content type
{
  • "form_id": 2,
  • "desired_fields": [
    ],
  • "included_records": [
    ]
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ],
  • "links": {
    }
}

Get unregistered portal participants

What it does: Retrieves records for participants who have not yet registered for the client portal. This is used to manage portal invitations and track which clients need portal access setup.

When to use it:

  • Identify clients who haven't registered for the portal
  • Generate portal invitation lists
  • Track portal onboarding progress
  • Send reminder emails to unregistered participants
  • Report on portal adoption rates

What you'll need:

  • Form ID, desired_fields, paging, and sorting (all required - the handler reads paging/sorting values unconditionally)
  • Program scoping, if needed, is done via the nested filtering.program field (there is no top-level program_id field)
  • Access token with apricot.read scope

What you'll get back: Records for participants without portal registration.

Authorizations:
authorizer-lambdaapi_key
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Request Body schema:
required
form_id
required
integer <int32>

The tier 1 form id to query against

desired_fields
required
Array of strings

The fields that will be included in the result. Must contain name (i.e. field_123_first, field_123_last) and email fields, minimum

exclusions
Array of integers <int32> [ items <int32 > ]

The document ids to exclude from the result; these have already been registered in Portal

last_mod_date
string or null

If present, only retrieve data records with a mod_time greater than or equal to this 'YYYY-MM-DD' value

searching
string or null

If present, the desired_fields array will have sql like applied to each field with this string

required
object

Pagination for the results. Required - requests without both page_size and page_num will fail.

required
object

Sort order for the results. Required - requests without both sort_col and sort_asc will fail.

object or null

Optional filters to narrow the queried records. Use filtering.program to scope by program (there is no top-level program_id field)

paginatedResult
boolean

Whether the results should be paginated

condensed_record_query
boolean
Default: false

If true, skips mapping the raw query results into fully-formed record objects

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotRecord)

Array of record objects

Request samples

Content type
{
  • "form_id": 0,
  • "desired_fields": [
    ],
  • "exclusions": [
    ],
  • "last_mod_date": "string",
  • "searching": "string",
  • "paging": {
    },
  • "sorting": {
    },
  • "filtering": {
    },
  • "paginatedResult": true,
  • "condensed_record_query": false
}

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Apricot Forms

Get list of forms

Retrieves a list of all forms available in your Apricot instance. Forms are the templates used to collect data in records.

Common use cases:

  • Discover available forms in your database
  • Get form IDs for creating/querying records
  • List all active forms for UI dropdowns
  • Export form definitions

Response includes:

  • Form ID (needed for records API)
  • Form name and description
  • Active status
  • Creation and modification timestamps
Authorizations:
authorizer-lambdaapi_key
query Parameters
required
object
Example: page[number]=1&page[size]=20

Pagination parameters for the result set. Always pass at least page[size] to keep response sizes predictable.

sort
string
Examples:
  • sort=sort_order - Sort by the form's configured display order (the default)

Sort order for results. Prefix with '-' for descending order. Defaults to sort_order if omitted.

object
Example: filter[active]=1

Filter parameters to narrow down results. For this endpoint, only active, parent_id, and is_visible_in_mobile are filterable

include
string
Example: include=all

Comma-separated list of related data to include in each form's attributes. Supported values are all and rules; either value causes both sections (with nested fields) and rules to be included

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

object (ApricotLinks)

Hypermedia links for navigation and resource discovery

Array of objects (ApricotShallowForm)

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/forms?page[size]=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "links": {
    },
  • "data": [
    ]
}

Get a specific form by ID

What it does: Retrieves detailed information about a specific form, including its complete structure, sections, field definitions, field types, and all field IDs. This is essential for working with records and attachments.

When to use it:

  • Discover field IDs for attachments (critical for using attachment endpoints)
  • Get complete form structure before creating records
  • Understand form configuration and field types
  • Find field IDs for dynamic field names (field_827, field_950, etc.)
  • Identify required fields vs optional fields
  • Export form definitions

What you'll need:

  • Form ID (get from GET /apricot/forms)
  • Access token with apricot.read scope

What you'll get back: Complete form structure including:

  • Form metadata (name, description, active status)
  • Sections array with all form sections
  • Fields array within each section containing:
    • id - The field ID to use in record creation (e.g., 827 → field_827)
    • field_type_id - Identifies the field type (text, dropdown, date, attachment, etc.)
    • label - Human-readable field name
    • is_required - Whether the field is mandatory
    • field_options - Available options for dropdown fields

CRITICAL: Finding Attachment Field IDs

This endpoint is essential for discovering which field IDs are attachment fields.

Steps to find attachment field IDs:

  1. Call GET /apricot/forms/{form_id}
  2. Look through the sections array
  3. Within each section, look through the fields array
  4. Find fields where field_type_id indicates an attachment field
  5. Note the id value - this is what you use for attachments

Example: If you find a field with id: 827 and it's an attachment type:

  • Use field_827 when creating records with attachments (multipart/form-data)
  • Use /records/{record_id}/attachment/827 for the attachment endpoint

Field Type Reference: Common field_type_id values:

  • 1 = Text
  • 2 = Numeric
  • 11 = Email
  • 17 = Date
  • 22 = Dropdown/Select
  • 26 = Checkbox
  • 29 = Attachment/File Upload
  • (Other types exist - check your form response)

Important notes:

  • Field IDs are unique to each form
  • Section IDs organize fields but aren't used in record creation
  • The sections.fields array shows fields in display order
  • Field options array shows available choices for dropdowns
  • Reference tags can be used as aliases for field IDs
Authorizations:
authorizer-lambdaapi_key
path Parameters
required
integer or string

Form ID. If the value is not numeric, it is looked up as the form's reference_tag instead

header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

object (ApricotLinks)

Hypermedia links for navigation and resource discovery

Array of objects (ApricotForm)

Array of form objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/forms/2" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get all fields for a specific form

What it does: Retrieves a complete list of all fields within a specific form. This is the most efficient way to get just the field definitions without the full form structure.

When to use it:

  • Quickly retrieve field IDs and types for a form
  • Build dynamic forms in your UI based on field definitions
  • Validate field data before creating records
  • Discover attachment field IDs (look for field_type_id indicating attachments)
  • Map field IDs to human-readable labels
  • Export field schemas for documentation

What you'll need:

  • Form ID (get from GET /apricot/forms)
  • Access token with apricot.read scope

What you'll get back: Array of field objects containing:

  • id - The field ID to use in records (e.g., 827 becomes field_827)
  • field_type_id - Field type identifier (text, number, date, attachment, etc.)
  • label - Human-readable field name shown in UI
  • is_required - Whether the field is mandatory
  • field_options - Available options for dropdown/multi-select fields
  • section_id - Which section the field belongs to
  • tooltip - Additional guidance for the field

Note: This endpoint returns fields only. For complete form structure including sections and metadata, use GET /apricot/forms/{id} instead.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
object (ApricotMeta)

Metadata about the response, including pagination information

Array of objects (ApricotFormField)

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/forms/2/fields" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "meta": {
    },
  • "data": [
    ]
}

Get programs associated with a form

What it does: Retrieves the programs that have access to a specific form, filtered to the programs the authenticated caller has access to (unless the caller is an admin). A caller with no accessible programs receives an empty array, even if the form is actually linked to programs. In Apricot, forms can be shared across multiple programs, allowing different service areas to use the same data collection structure.

When to use it:

  • Discover which programs use a specific form
  • Verify form accessibility across programs
  • Understand form sharing and reuse patterns
  • Check if a form is available in a target program
  • Audit program-form associations
  • Plan program restructuring or consolidation

What you'll need:

  • Form ID (get from GET /apricot/forms)
  • Access token with apricot.read scope

What you'll get back: Array of program objects that have access to this form and that you (the caller) can access, each containing:

  • Program ID
  • Program name
  • Program active status
  • Program description
  • Program metadata

Common scenarios:

  • A "Client Intake" form shared across multiple service programs
  • An "Assessment" form used by different departments
  • Standard forms replicated across regional programs
  • Universal forms available to all programs

Use this to:

  • Determine if you need to associate a form with additional programs
  • Understand data collection scope across your organization
  • Identify forms that are widely shared vs. program-specific
  • Plan form assignments when creating new programs
Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/forms/2/programs" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get user's accessible programs for a form

What it does: Retrieves the list of programs that the authenticated user has access to for a specific form. This helps determine which programs a user can create or view records in for the given form.

When to use it:

  • Build dynamic UI showing which programs a user can access for a form
  • Validate user permissions before creating records
  • Filter program dropdowns based on user access
  • Implement role-based access control in your application
  • Display personalized program lists

What you'll need:

  • Form ID
  • Access token (identifies the user automatically)

What you'll get back: Array of programs the authenticated user can access for this form.

Authorizations:
authorizer-lambdaapi_key
path Parameters
id
required
integer <int32>
header Parameters
Authorization
required
string

Header that carries token for request Authorization

Responses

Response Schema: application/json
Array of objects (ApricotProgram)

Array of program objects

Request samples

curl -X GET "https://api.bonterra.network/v1/apricot/forms/2/userPrograms" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response samples

Content type
application/json
{
  • "data": [
    ]
}