Skip to content
Back to Steward

Applications

An application is the core KYC/KYB record for a customer you are onboarding. Every application is one of two entity types (Business or Individual) and moves through a four-state lifecycle.

Status Meaning Set by
PENDING Created, not yet submitted for review POST /applications (initial state)
SUBMITTED Submitted and awaiting a review decision POST /applications/{id}/submit (only from PENDING)
APPROVED Review passed POST /applications/{id}/approve (only from SUBMITTED)
REJECTED Review failed POST /applications/{id}/reject (only from SUBMITTED)

The transition endpoints enforce the source state: submitting a non-PENDING application, or approving/rejecting one that isn’t SUBMITTED, returns a 400.

The corresponding bulk routes are POST /applications/bulk/submit, /approve, and /reject. They apply the same lifecycle rules and side effects as the single-application routes. Rows already in the requested target state are skipped; any other incompatible state rejects the whole request before a lifecycle write begins. Submission still records the caller and calculates a risk profile. Approval still checks UBO and director prerequisites and resolves pending-onboarding tasks. These synchronous workflows do not promise a database transaction around downstream infrastructure failures.

PATCH /applications/{id} updates application data (labels, display configuration, metadata, and the individual or business details). It does not change status. Status only moves through the submit / approve / reject endpoints.

POST /applications/bulk/update applies the same explicit update allowlist and nested merge behavior to each selected application through a patch object. Undeclared application fields are ignored rather than becoming writable. Rows whose resulting state already matches the patch are skipped without another write or audit entry; real identity edits retain the single-operation re-screen and risk-profile behavior.

metadata is a free-form key/value store for your own identifiers. It merges rather than replaces, and has its own delete semantics. See the Metadata guide before writing to it.

Creating an application with sendEmail: true sends the invitation email in the same request. If the email provider reports the address as suppressed (an earlier email to it bounced or was marked as spam), the application is still created and the 201 response includes inviteEmail with status: FAILED, failureCode: INACTIVE_RECIPIENT and a reason you can show your user. When the email goes out, inviteEmail.status is SENT. inviteEmail is absent when sendEmail is false. See the error-handling guide for other delivery failures.

GET /applications returns a cursor-paginated list (see the Pagination guide). GET /applications/{id} returns the full record, including the risk object (see the Risk Scoring guide) on the populated businessApplication or individualApplication.

withSteward is a boolean on list items and on GET /applications/{id}. It is separate from status. While it is true, Steward is reviewing the application: the list row stays, with the name, status, the caller’s own labels and reviewers, and updatedAt frozen at submission. The response does not include the application’s contents. Document, screening, and export reads respond 404 while it is with Steward. Notes, audit, and task list reads return an empty page. Customer approve, reject, archive, and hide respond 400 with This application is with Steward. Pass withSteward=true to list only those applications. It combines with status as an OR. The status enum is unchanged.

On an individual application, individualApplication.kycDataVerificationSkipReason is optional. When present, individual KYC did not run because a national ID is required in the applicant’s current residence country. The value uses the form KYC skipped because national ID is required in {country}. That denotes a check that did not run, not a failed match. The field is absent when the applicant’s country is no longer in that skip policy or a later Trulioo KYC result exists.

individualApplication.kycDataVerificationIncompleteReason is also optional. When present, the latest individual KYC run was incomplete because some of Trulioo’s data sources did not respond. The value uses the form KYC incomplete: {n} of {total} Trulioo data sources did not respond. KYC runs again when the application is resubmitted. The individual’s verifications keep the results from the sources that did respond, plus a row with field: "incomplete". The field is absent once a later KYC run completes.

Lists exclude hidden applications by default. Pass includeHidden=true to return visible applications together with applications hidden by your platform users; Steward-hidden applications are never returned. Pass hidden=true together with includeHidden=true to return only applications hidden by your platform users. Without includeHidden, the list ignores the hidden query parameter.

Hiding is a reversible display preference. It does not archive the application, stop screening, or change onboarding. POST /applications/{id}/hide and POST /applications/bulk/hide remove the selected applications from the default list, dashboard counts, open-task queues, and search. POST /applications/{id}/unhide and POST /applications/bulk/unhide reverse that.

Customer API keys hide as CLIENT. They cannot see, hide, or unhide applications Steward has hidden; those ids are reported as not found rather than forbidden, so the existence of a Steward-hidden row is not disclosed. Staff JWT sessions keep their existing Steward provenance and may still act on those rows.

Already-hidden (or already-visible) applications are skipped, not rewritten. The response lists applicationIds that changed and skippedApplicationIds that were already in the requested state, with changedCount / skippedCount.

GET /applications/{id}/hide-summary and POST /applications/bulk/hide-summary preview child-application and open-task counts hiding would affect. They use the same visibility rules as the mutation.

POST /applications/bulk/hide accepts an optional reason. It is trimmed; blank text is treated as absent. A supplied reason replaces the generic “Hidden by platform user” note on each changed application and in the audit log (at most 1000 characters). Unhide does not take a reason.

Read-only users cannot hide or unhide.

POST /applications/bulk/reviewers adds the given reviewer ids to each selected application. Reviewers already assigned there stay assigned. Applications that already carry every requested reviewer are skipped.

POST /applications/bulk/label adds or removes one label across the selection (action: add or remove). Applications already in that state are skipped. There is no wholesale replacement of a row’s label set.

Every bulk applications request, including hide/unhide, accepts at most 500 application id entries. Repeated ids are coalesced before processing. A missing id returns 404 for the whole request; an id outside the API key’s account returns 403 for the whole request. Steward-hidden ids also return 404 to customer callers. Nothing is written in these cases: the entire selection is checked before any mutation, and account-scoped route metadata on the array is not itself that check.

Terminal window
# Hide several applications, with an optional reason
curl -X POST https://api.getsteward.ai/api/v1/applications/bulk/hide \
-H "Authorization: Bearer stw_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"applicationIds":["{id}"],"reason":"Duplicate of another onboarding"}'
# Add reviewers without replacing existing ones
curl -X POST https://api.getsteward.ai/api/v1/applications/bulk/reviewers \
-H "Authorization: Bearer stw_live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"applicationIds":["{id}"],"reviewerIds":["{reviewerId}"]}'

An application can be exported in four formats: GET /applications/{id}/export/{csv,json,pdf,xml}. See the Exports guide for the shared include toggles and the XML templates.

An application is either a primary onboarding or a related party of another one: a UBO, director, or shareholder pulled out of a parent’s ownership structure. Two fields tell them apart:

Field Meaning
parentApplicationId The application this one hangs off. Absent when this is a primary onboarding.
parentRoles The roles this entity holds in that parent. Only present alongside a parent id.

Both appear on businessApplication and individualApplication, and both are computed at read time rather than stored. The absence of parentApplicationId is the signal for “this is a top-level application”, which is useful when a webhook fires and you need to decide whether the event concerns a customer you onboarded directly or one of their related parties.

The two entity types draw parentRoles from different role sets (an individual can be a DIRECTOR of its parent; a business cannot), so do not assume one shared list of values across both.

The submitter is the person completing the onboarding flow, distinct from the entity being onboarded. PATCH /applications/{id}/submitter corrects their firstName, lastName, and email after the application exists. The optional phoneNumber, role and capacity are three-way: omit one to keep what is stored, send null to clear it, or send a value to set it.

The edit is scoped to this application: where a submitter is shared across several applications, updating one leaves the others untouched. Pass resend to re-send the onboarding link to the corrected address in the same call, without waiting for the application to be back in PENDING.

With resend, the response includes inviteEmail with the same SENT or FAILED outcome. The bulk endpoint lists applications whose resend failed in emailFailedApplicationIds; those ids also stay in applicationIds, because the submitter update itself succeeded.

POST /applications/bulk/update-submitter applies one patch containing the same submitter fields to every selected application. Email addresses are normalized. Shared submitters are copied before editing, missing submitters are created, and proxy-email suppression is cleared. Identical contact details are skipped unless suppression must change or resend: true is requested; an explicit resend is never silently suppressed. Inline-onboarded child applications keep the single-operation resend restriction.

Terminal window
# List applications (cursor-paginated)
curl https://api.getsteward.ai/api/v1/applications \
-H "Authorization: Bearer stw_live_your_api_key_here"
# Fetch one, including its risk breakdown
curl https://api.getsteward.ai/api/v1/applications/{id} \
-H "Authorization: Bearer stw_live_your_api_key_here"
# Submit it for review (only valid from PENDING)
curl -X POST https://api.getsteward.ai/api/v1/applications/{id}/submit \
-H "Authorization: Bearer stw_live_your_api_key_here"

GET /audit-logs?applicationId={id} returns an application’s change history: a cursor-paginated list of audit entries, each a timestamped event (created, submitted, approved, document uploaded, risk score updated, and so on) with the actor’s name and an optional note. Use it to reconstruct everything that has happened to an application.

Existing fields keep their current meaning, so nothing breaks for current integrations.

Each item also carries who acted (staff, applicant, system or unknown) and the structured event detail.

actor.kind is STAFF, APPLICANT, SYSTEM, or UNKNOWN. When available, actor.id and actor.name identify the performer. performedByName and performedById retain their existing meanings.

An applicant’s answer to an information request is attributed by name on the public list, where the tab shows it as Applicant · Name.

payload is an extensible display summary for newer entries. It may gain fields, so use it for display and do not depend on its exact shape. Older entries may omit it; their existing note remains available.

The platform tab’s legacy rich-text rendering field and filter-menu counts are not returned by this endpoint.

All filters are optional except applicationId. Different filters combine; repeated events and performers values match any of the supplied values.

  • events: Include only these public event types. Repeat for multiple event types.
  • performers: Include entries for these actor keys. Use a performer ID when the actor has one; otherwise use the lowercase actor kind, such as applicant or system. Repeat for multiple keys.
  • from: Inclusive lower bound for createdAt, as an ISO 8601 date-time.
  • to: Inclusive upper bound for createdAt, as an ISO 8601 date-time.
  • search: Case-insensitive search across the event label, structured payload, or legacy note. Up to 200 characters.
  • excludeHidden: Set to true to omit hidden entries. Hidden entries are included by default.

For example, filter to selected events and performers in a date range, match text, and omit hidden entries:

Terminal window
curl -G https://api.getsteward.ai/api/v1/audit-logs \
-H "Authorization: Bearer stw_live_your_api_key_here" \
--data-urlencode "applicationId={id}" \
--data-urlencode "events=CREATED" \
--data-urlencode "events=NOTE_CREATED" \
--data-urlencode "performers={userId}" \
--data-urlencode "performers=applicant" \
--data-urlencode "from=2026-05-01T00:00:00.000Z" \
--data-urlencode "to=2026-05-31T23:59:59.999Z" \
--data-urlencode "search=passport" \
--data-urlencode "excludeHidden=true"

GET /platform/search?q=… runs an account-scoped global search and returns matches grouped into applications, screening, documents, and tasks sections, each with an items array and a total. Narrow it with type (applications / screening / documents / tasks) and the per-category filters. Every result carries the applicationId it belongs to, so search is the usual way to find the application you then read in full.

GET /applications?search=… filters the application list itself. The first page of a search (no cursor) also carries total, the number of applications the search returns across every page; later pages and requests without search omit it.