Application Status Changes API Guide
Guide document for using our POST /api/v1/application_status_changes endpoint
Overview
- Endpoint:
POST /api/v1/application_status_changes - Purpose: Create a status change for an application (e.g. mark as withdrawn, denied, closed).
- Required body fields:
application-id,status. - Many other fields are optional, but statuses that require a reason or date will return validation errors if those fields are missing. Valid values for
status-reasondepend on the targetstatus.
What to send: slugs vs human-readable strings
status-reason: Send the exact human-readable string (e.g."Caregiver chose to withdraw themselves"). The API expects the full string, not a slug.overridden-reason: Send a slug (e.g.requirement_completed_offline). See the table below for slug → description.status,appeal-location,contact-method: Send slugs (e.g.withdrawn,office_of_administration,certified_mail).
Example: successful withdrawal request
{
"data": {
"attributes": {
"application-id": 12345,
"status": "withdrawn",
"status-date": "2025-06-15",
"status-reason": "Caregiver chose to withdraw themselves",
"status-explanation": "Family decided to pursue other options.",
"not-signed": true
}
}
}Response (201 Created):
{
"data": {
"id": "987",
"type": "application-status-changes",
"attributes": {
"application-id": 12345,
"status-before": "recruiting",
"status-after": "withdrawn",
"was-overridden": false,
"overridden-reason": null,
"overridden-explanation": null,
"license-external-identifier": "LIC-2025-001",
"provider-external-identifier": null,
"updated-at": "2025-06-15T14:30:00.000Z"
}
}
}Target status values
Pass the slug for status. Allowed values:
| Status |
|---|
applying |
approved |
awaiting_referral |
closed |
denied |
eligible_for_conversion |
in_adoption_process |
inactive |
recruiting |
recruiting_dropout |
ready_for_review (this is only for certain agencies - if this isn't familiar to you, you can ignore this option!) |
withdrawn |
Status-specific requirements
Statuses that require a reason and explanation
For these statuses you must send status-reason, status-explanation, and status-date (see next subsection) or the API will return validation errors:
withdrawndeniedrecruiting_dropoutclosedin_adoption_process
Statuses that require a date
For these statuses you must send status-date (ISO 8601 date):
approvedcloseddeniedinactiverecruiting_dropoutwithdrawn
Statuses that do not require a reason
For statuses that do not require a reason (e.g. approved, recruiting, applying, eligible_for_conversion, inactive), you must not send status-reason or status-explanation. If you send either when changing to one of these statuses, the API returns 422 with validation errors.
Example: approving an application (no reason required)
{
"data": {
"attributes": {
"application-id": 12345,
"status": "approved",
"status-date": "2025-06-15",
"date-application-started": "2025-01-10"
}
}
}Bad example: sending status-reason when changing to approved
Do not include status-reason or status-explanation for statuses that do not require them.
{
"data": {
"attributes": {
"application-id": 12345,
"status": "approved",
"status-date": "2025-06-15",
"status-reason": "Other"
}
}
}Response (422):
{
"errors": [
{
"detail": "status-reason is not allowed when changing to approved",
"source": { "pointer": "/data/attributes/status-reason" }
}
]
}status-reason by target status
Send the exact human-readable string in the request (the API does not use slugs for this field). Copy the value exactly; allowed values depend on the target status. If you send a status-reason that is valid for a different status (e.g. a withdrawn reason when changing to denied), the API returns 422 with an error indicating the reason is not valid for the target status.
withdrawn
- Caregiver chose to withdraw themselves
- Minor was placed or seems like will be placed with another applicant
- Reunification with parent(s)
- Placement moved to permanency
- Caregiver withdrew due to complaints/delays with the process
- Caregiver withdrew because of criminal background
- Life circumstance change
- Caregiver was uncooperative/non-responsive
- Referred/Applying with another agency
- Provider chose to withdraw themselves
- Provider was uncooperative/non-responsive (application ceased)
- Provider withdrew because of leadership challenges
- Provider withdrew due to complaints/delays with the process
- Other
Note: Some agency types (e.g. CPA/Congregate) may only allow a subset of these; the API will reject invalid combinations.
denied
- Buildings and Grounds standards not met
- Non-exemptible conviction
- Inimical/hostile/unfriendly conduct during process
- Caregiver was uncooperative/non-responsive
- Provider was uncooperative/non-reponsive (application ceased)
- Leadership did not pass background checks
- Other
Note: Some agency types may only allow a subset; the API will reject invalid combinations.
recruiting_dropout
- Binti application created in error
- Not enough capacity
- Not ready yet
- Not located in county
- No longer interested
- Unresponsive
- Specific child no longer needs placement
- Referred/Applying with another agency
- Other
closed
- Adoption/Permanency finalized
- Change in family dynamic/structure
- Dissatisfied with agency support
- Financial reasons
- Health reasons
- Approval revoked
- Moved
- Negative parenting experience
- Retiring
- Specific child left
- Unwilling to comply with regulations
- Surrendered
- Rescinded
- Declined conversion
- Other
Note: Some agency types (e.g. CPA/Congregate) may only allow a subset; the API will reject invalid combinations.
in_adoption_process
- Adoption
- Ineligible
- Legal Guardianship
- Not interested
- Unresponsive
- Binti application created in error / duplicate
- Other
Bad example: sending a reason that belongs to a different status
This request tries to withdraw but uses a denied reason:
{
"data": {
"attributes": {
"application-id": 12345,
"status": "withdrawn",
"status-date": "2025-06-15",
"status-reason": "Buildings and Grounds standards not met",
"status-explanation": "Withdrawn via API"
}
}
}Response (422) -- "Buildings and Grounds standards not met" is a valid denied reason, not a valid withdrawn reason:
{
"errors": [
{
"detail": "is not a valid reason for the status withdrawn",
"source": { "pointer": "/data/attributes/status-reason" }
}
]
}overridden-reason and overrides
When a caseworker or Binti admin overrides a status change (e.g. to bypass a validation), they can send overridden-reason and optionally overridden-explanation.
Pass the slug in the request. Allowed values for overridden-reason:
| Slug (pass this value) | Description |
|---|---|
requirement_completed_offline | Requirement was completed offline |
required_field_not_required_for_applicant | Required field was not required for this applicant |
requirement_fulfilled_by_alternative_means | Requirement was fulfilled by alternative means |
state_or_court_granted_exception | State or court granted an exception |
historical_application_migrated_to_binti | Historical application was migrated into Binti |
bug_requirements_are_complete | Bug: requirements are complete |
other | Other |
Use overridden-explanation (free text) to provide context when needed.
Example: approving with an override
When the API returns overridable validation blockers and your user has override permission, retry with overridden-reason and overridden-explanation:
{
"data": {
"attributes": {
"application-id": 12345,
"status": "approved",
"status-date": "2025-06-15",
"date-application-started": "2025-01-10",
"overridden-reason": "requirement_completed_offline",
"overridden-explanation": "Home study documents were completed on paper and verified in person."
}
}
}Bad example: sending override params without permission
If meta.current_api_user_can_override was false on a prior 422, do not include overridden-reason or overridden-explanation:
{
"data": {
"attributes": {
"application-id": 12345,
"status": "approved",
"status-date": "2025-06-15",
"overridden-reason": "other",
"overridden-explanation": "Override attempt without permission"
}
}
}Response (422):
{
"errors": [
{
"detail": "You do not have permission to override validation for this status change. Do not send overridden-reason or overridden-explanation.",
"source": { "pointer": "/data/attributes/overridden-reason" }
}
]
}Validation blockers and overrides
When the API returns 422 for a status change, some errors include a meta object with type, optional link, and overridable. Use meta.overridable to decide whether to retry with overridden-reason and overridden-explanation to bypass the blocker, or to fix the issue first.
Blockers you can override: When meta.overridable is true, retry the request with overridden-reason (and optionally overridden-explanation) to bypass that blocker. Examples of overridable blockers:
- Missing or incomplete documents (forms, supporting documents)
- Background checks incomplete or failed
- References incomplete
- Trainings incomplete
- Child in home when changing to closed/withdrawn/recruiting_dropout (must resolve placement first, or override)
- Incomplete family type
- Provider ID or Family ID (or RFA ID) required for approval
- Status change not permitted for your role (unpermitted status change)
Blockers you must fix first: When meta.overridable is false (or meta is absent for that error), resolve the issue before the status change will succeed. Examples:
- Incomplete applicant or agency profile data (e.g. address, capacity, ages, primary language)
- The chosen status is not allowed for this application type
- Other validations that cannot be overridden
Who can override? API users do not automatically have permission to override. If you would like to be able to override validation blockers via the API, you need to go through the same process for getting that access as you would for the UI flow.
Sending override params without permission: If your API user does not have override permission (e.g. meta.current_api_user_can_override is false on a prior 422), do not send overridden-reason or overridden-explanation. If you send either when you cannot override, the API returns 422 with an error stating that you do not have permission to override and that you must not send those attributes.
Example: 422 response with overridable and non-overridable blockers
This shows what a typical 422 response looks like when multiple validation blockers are present. Note the top-level meta.current_api_user_can_override and the per-error meta with type, overridable, and optional link:
{
"errors": [
{
"detail": "Background checks are incomplete",
"source": { "pointer": "/data/attributes/incomplete-background-checks" },
"meta": {
"type": "incomplete_background_checks",
"overridable": true,
"link": "/admin/applications/12345/background_check_records"
}
},
{
"detail": "Documents are incomplete",
"source": { "pointer": "/data/attributes/incomplete-documents" },
"meta": {
"type": "incomplete_documents",
"overridable": true,
"link": "/admin/applications/12345/documents"
}
},
{
"detail": "Applicant profile is incomplete",
"source": {
"pointer": "/data/attributes/applicant-agency-profile/incomplete-applicant-data"
},
"meta": {
"type": "incomplete_applicant_data",
"overridable": false,
"link": "/admin/applications/12345/edit?validate_for_status=approved"
}
}
],
"meta": {
"current_api_user_can_override": true
}
}In this example the first two errors are overridable (you can retry with overridden-reason), but the third is not -- you must fix the applicant profile data before the status change can succeed.
Other status-relevant fields
appeal-location and contact-method
Relevant when changing to statuses that support appeals: denied and closed. Pass the slug.
- appeal-location slugs:
office_of_administration,state_hearings_division - contact-method slugs:
certified_mail,email,in_person,other
not-signed
Optional boolean. Allowed when changing to denied or withdrawn to indicate the applicant did not sign. For other statuses (e.g. closed), if you send not-signed, the API accepts the request and stores not_signed as false, no matter what, effectively ignoring it.
Mutually exclusive with date-application-started: You cannot have both "not signed" and a date application started. If you send not-signed as true, the API clears date-application-started (same as the UI, where checking "Not signed" clears "Date application signed"). Do not send both in the same request. If you do send both, the API returns 422 with a schema validation error and a clear detail message.
Bad example: sending both not-signed and date-application-started
{
"data": {
"attributes": {
"application-id": 12345,
"status": "withdrawn",
"status-date": "2025-06-15",
"status-reason": "Other",
"status-explanation": "Withdrawn via API",
"not-signed": true,
"date-application-started": "2025-01-10"
}
}
}Response (422):
{
"errors": [
{
"detail": "You included both date-application-started and not-signed, which is not allowed. Send only one.",
"source": { "pointer": "/data/attributes" }
}
]
}status-date and status-explanation
- status-date: ISO 8601 date. Required for the statuses listed in "Statuses that require a date" above.
- status-explanation: Free text. Required for the statuses listed in "Statuses that require a reason and explanation" above.
Optional context fields
- date-application-started – Date the application was started (ISO 8601). Mutually exclusive with not-signed: if you send
not-signedastrue, the API clears this field. Do not send both; sending both triggers 422 with the schema error message above. - notification-date – Notification date (ISO 8601).
Use these when they apply to your workflow (e.g. notification context).
Renewal-only fields (independent)
These two fields are independent: you can send one, the other, or both when their respective conditions are met.
- next-renewal-due-date – Only allowed when changing a renewal application to approved. Use it to set the date the next renewal is due. If you send it in any other scenario (e.g. approving an initial, or changing to denied), the API returns 422.
- selected-renewal-template-lineage-slug – Only allowed when changing status to approved, the agency has renewal template selection enabled, and there is more than one available renewal template lineage. Pass the slug of the desired renewal template lineage (not an ID). The slug must match a renewal template lineage for the application agency that is available for that application. If you omit this field, the current template lineage is left unchanged. If you send it when the conditions are not met or with an invalid or unavailable slug, the API returns 422. This is a relatively niche use case, so it may not be relevant to your agency.
Bad example: sending next-renewal-due-date when not approving a renewal
{
"data": {
"attributes": {
"application-id": 12345,
"status": "denied",
"status-date": "2025-06-15",
"status-reason": "Other",
"status-explanation": "Denied via API",
"next-renewal-due-date": "2026-06-15"
}
}
}Response (422):
{
"errors": [
{
"detail": "next-renewal-due-date is only allowed when approving a renewal application.",
"source": { "pointer": "/data/attributes/next-renewal-due-date" }
}
]
}Response and errors
- Success: The API returns 201 Created. The response body represents the created application status change (e.g.
id,application-id,status-before,status-after,updated-at, and related attributes). See the Application Status Changes schema in the API reference for the full response shape. - Validation errors: The API returns 422 Unprocessable Entity with error details when, for example, a required field is missing for the chosen status, a
status-reason(or other enum value) is not allowed for that status, or you sendstatus-reasonorstatus-explanationfor a status that does not allow them. Use the response body to correct the request.
For 422 validation errors, the response includes a top-level meta object with:
current_api_user_can_override(boolean) – Whether the authenticated API user has permission to override validation blockers. Iftrue, you may retry withoverridden-reason(and optionaloverridden-explanation) when an error hasmeta.overridable === true. Iffalse, you must fix the issues; sending override parameters will not bypass blockers.
Each error object may also include a meta object with:
type– Identifier for the kind of blocker (for grouping or display).overridable– Boolean; whentrue, the blocker can be bypassed by retrying withoverridden-reason(and optionaloverridden-explanation); whenfalse, the issue must be fixed before the status change can succeed.link– Optional URL where the issue can be addressed (when applicable).
Schema validation errors (e.g. missing required field, invalid enum value) may not include meta; fix those by correcting the request body. Some schema validation errors (including the mutual-exclusivity rule for date-application-started and not-signed) return a descriptive detail message to help you correct the request.
Bad example: missing required fields
This request omits application-id (required):
{
"data": {
"attributes": {
"status": "withdrawn"
}
}
}Response (422):
{
"errors": [
{
"detail": "object at `/data/attributes` is missing required properties: application-id",
"source": { "pointer": "/data/attributes/application-id" }
}
]
}Bad example: invalid enum values
This request uses invalid values for multiple enum fields:
{
"data": {
"attributes": {
"application-id": 12345,
"status": "invalid_status",
"status-reason": "Not a valid reason string",
"overridden-reason": "invalid_reason",
"appeal-location": "invalid_appeal_location",
"contact-method": "invalid_contact_method"
}
}
}Response (422) -- one error per invalid enum field:
{
"errors": [
{
"detail": "value at `/data/attributes/status` is not one of: [\"applying\", \"approved\", ...]",
"source": { "pointer": "/data/attributes/status" }
},
{
"detail": "value at `/data/attributes/status-reason` is not one of: [\"Caregiver chose to withdraw themselves\", \"Minor was placed or seems like will be placed with another applicant\", ...]",
"source": { "pointer": "/data/attributes/status-reason" }
},
{
"detail": "value at `/data/attributes/overridden-reason` is not one of: [\"requirement_completed_offline\", \"required_field_not_required_for_applicant\", ...]",
"source": { "pointer": "/data/attributes/overridden-reason" }
},
{
"detail": "value at `/data/attributes/appeal-location` is not one of: [\"office_of_administration\", \"state_hearings_division\"]",
"source": { "pointer": "/data/attributes/appeal-location" }
},
{
"detail": "value at `/data/attributes/contact-method` is not one of: [\"certified_mail\", \"email\", \"in_person\", \"other\"]",
"source": { "pointer": "/data/attributes/contact-method" }
}
]
}Updated about 1 month ago
