This page is intentionally not listed in the documentation navigation. The
Application APIs are in private preview and this page is shared by direct link
only. Endpoints described here that are not yet generally available are likewise
excluded from the REST API reference.
Overview
The Application APIs let you own the apply experience in your own product rather than launching the hosted flow described in Apply for a loan or credit card. Most partners should use the SDK. This surface exists for partners who have agreed with Imprint to render the application themselves, and it carries additional compliance obligations — you own the disclosure presentation and must pass a compliance review before production. All twelve endpoints require thex-imprint-merchant-key header. That header is
the only thing that names your program — there is no product_id in any request
body. The key must be authorized for the named program, and a product-bound key
does not make the header optional. All twelve operations, including reads, require
APPLICATION_WRITE; APPLICATION_READ alone does not grant access to this preview.
The two things to understand first
Everything else on this page follows from these two ideas.Configuration drives form construction; the application drives form progression
There are two sources of truth and they answer different questions.GET /v2/application_configuration says what your program can ever ask for — the
field set, their validation rules, the disclosures, whether phone verification is
Imprint’s or yours. Cache it by configuration_revision, and fetch the revision
pinned on an application when resuming that application. Build your form components
from it.
The requirements array on an application says what is outstanding for this
applicant right now. Read it after every write to decide what to render next.
Two guarantees hold between them:
- An application never names a
keythat is absent from the configuration. - The configuration never asserts what is required for a given application. A
CONDITIONALLY_REQUIREDfield may or may not be asked for.
fields, DISCLOSURE keys in disclosures, and ACTION
or SELECTION keys in requirement_definitions. Those three collections together
contain every key the application can return.
Requirements are a list, not a set of fields
Fields, disclosures, selections, and actions all appear as entries in one array, each with akind, a status, and the milestone it blocks:
Because everything is a list entry, a new requirement is an added array element,
not a new response field. Handle unknown
kind and key values by ignoring
them. A client that switches exhaustively on kind and throws on the default will
break the first time a program adds a requirement; one that skips what it does not
recognize keeps working.
The endpoints
The four marked idempotent accept an
Idempotency-Key header retained for 24
hours. Replaying a key returns the original semantic result instead of doing the
thing twice. A request still running returns 409 IDEMPOTENCY_IN_PROGRESS with Retry-After; reusing the key for a different
operation, target, action, or body returns 409 IDEMPOTENCY_KEY_REUSED.
A typical successful flow calls 1, 3, 6, 7, 5, 9, 10, 4 (polling), then 12.
1. Retrieve the application configuration
revision for current configuration. When
resuming an application, pass its configuration_revision as revision so field,
requirement, and disclosure definitions match the pinned application.
configuration_revision, not only by product. Ordinary
configuration and disclosures remain pinned for an in-flight application. If a
legal revision is revoked, the server assigns the replacement revision while
preserving candidate and consent history, and returns the replacement disclosure as
unsatisfied. Refetch the application’s new configuration_revision and present it
before retrying. Product shutdowns still take effect immediately.
Fields
type chooses the input, pattern / min_length / max_length / allowed_values
give you client-side validation identical to what the server enforces, and
necessity is one of ALWAYS_REQUIRED, CONDITIONALLY_REQUIRED, or OPTIONAL.
No label is published, deliberately. What a field is called belongs to the
surface it appears on — it changes with that surface and needs translating alongside
the rest of your page. Validation is published because the program enforces it
server-side, and a client inventing its own would reject input the program accepts.
necessity: CONDITIONALLY_REQUIRED means “sometimes.” Do not try to work out when.
The application’s requirements array tells you, per application.
Disclosures
url always points at the document. When wording is legally significant the
program also publishes body — an array of paragraphs you must render verbatim,
in order — and acknowledgement, the text that goes next to the consent control. An
e-signed authorization is the case that forces this: a link alone is not enough for
text that must appear on the page.
gates says which milestone the consent unlocks. An SMS disclosure gates
PHONE_VERIFICATION and must be consented before a code can be sent.
legal_documents is a separate, flat list for a “documents” or “terms” section. It
is not consent — nothing in it needs acknowledging.
Password policy
Publish-what-you-enforce:min_length, max_length, a pattern, and a per-locale
description you can show under the input.
Applicant-facing copy is a locale dictionary
Everymessage a shopper reads — description above, and the message on
requirements, resolution options, and outcome reasons — is a dictionary keyed by
IETF locale tag rather than a single string:
en-US, then to any locale present. A missing translation must degrade to
readable copy in the wrong language, never to a blank where the applicant most
needs to be told something.
Error bodies are plain English strings. They are developer-facing, and a client
rendering a raw API error to a shopper has a bug in any language.
2. List applications
IN_PROGRESS application for that
partner_customer_id.
3. Create an application
PATCH. There is no product_id — x-imprint-merchant-key already names
the program, and a body field naming it again would be a second source of truth with
no stated precedence.
phone_verification
requirement. The response is the whole application, so you have your first render
without a follow-up GET.
4. Retrieve an application
poll_after_ms
milliseconds between calls rather than fixing your own interval. It is null when no
evaluation is in flight — polling again will not change the result.
credit_limit is in minor units, so 530000 is $5,300.00.
expires_at is null once the application is submitted. It governs
abandonment of an unsubmitted application, and there is nothing left to abandon. Do
not read it as a deadline for accepting an offer — the offer carries its own
expires_at.5. Update an application
null clears a field. A
multi-page form can send only the current page’s delta without wiping earlier pages.
The rule is recursive: applicant.address.city: null clears only the city, while
applicant.address: null clears the address. metadata merges by key, a null
metadata value removes that key, and metadata: null clears the map. Zero remains a
value rather than being treated as missing. consents is additive: omission or []
adds nothing, while consents: null is rejected and never withdraws a receipt.
requirements array arrives with
the write. You never need to follow a PATCH with a GET.
annual_income is in minor units. consents names disclosure ids from the
configuration; recording one flips that DISCLOSURE requirement to SATISFIED. A
field the program rejects is safely retained as the editable candidate and comes
back as INVALID with a localized message, rather than failing the whole request.
Clear or replace it in a later patch. Malformed JSON, unknown or disallowed fields,
and wrong JSON types return 400 without mutating the application.
This endpoint carries everything the applicant typed and could retype. Anything
with an external side effect or a one-way transition — sending an SMS, verifying a
code, writing a credential, committing a decision — is a separate POST
sub-resource. That is the line the other seven writes sit on the far side of.
Applicant and identity data freeze at submit. Card design remains editable through
OFFERS_AVAILABLE, or can be supplied to accept, and freezes when acceptance
commits so issuance cannot race a later selection. A 409 means the requested field
is not editable in the application’s current phase, or the draft has expired.
6. Verify the applicant’s phone number
phone_verification.mode), not a property of the route — so the contract is
identical across partners.
SEND requires the SMS disclosure to be consented first. Until it is, that
disclosure requirement carries blocks: PHONE_VERIFICATION and this returns 409.
Note the PENDING in the response above — a code is out but unverified. That is the
state your “enter the code” screen renders from.
Size your code input from phone_verification.code_length. A wrong or expired code
is a 422; too many failed attempts is a 429.
Resends are rate-limited
retry_after_seconds is duplicated on the Retry-After header; it is in the body as
well so a client that already parses the JSON error can render a countdown without
reaching for response headers. The value rounds up — truncating would report 0
with time still on the clock and send you straight into another rejection.
Disable your resend control for phone_verification.resend_after_ms so the applicant
is not offered a button that cannot work yet. But treat the 429 as
authoritative — it accounts for a code sent before your page loaded, which your
configuration value cannot know about.
Every send is an SMS someone receives and someone pays for. Build the countdown.
Verifying may surface a new requirement
If the phone already belongs to an Imprint account, the response adds anaccount_ownership requirement, and its resolution_options names the ways forward.
See endpoint 8.
7. Prefill applicant data
409 means it is not verified yet.
key, so you can render a confirm-and-edit screen. Present them for confirmation
rather than submitting silently — the applicant must be able to correct a wrong
line.
A miss is a 404, and you must branch on type
NO_PREFILL_MATCH is an ordinary outcome, not a fault: send the applicant to manual
entry. The application is left untouched.
Branch on the error type, not the status alone. NO_PREFILL_MATCH is the
applicant’s path forward; APPLICATION_NOT_FOUND on the same status code means the
id was wrong and is your bug — it must not be shown to the applicant as though
their data went missing.
8. Verify ownership of an existing Imprint account
account_ownership requirement when the applicant’s phone number
already belongs to an Imprint account. Follow the advertised resolution option:
use PASSWORD for VERIFY_PASSWORD, or HOSTED for
HOSTED_AUTHENTICATION (including PIN-only accounts).
Not to be confused with phone_verification, which verifies the number. This
proves the applicant is the person who owns the account that number is attached to.
type is PASSWORD, not VERIFY_PASSWORD. The VERIFY_PASSWORD you saw in
resolution_options names which endpoint to call; it is not a value to pass
through as a field. See Resolution options.ownership_session is short-lived and appears only in the response that initiates
the hosted handoff. Do not persist the URL; ordinary GETs omit it. Completing the
handoff updates the authoritative application, which you retrieve by polling.
When the existing account has neither a password nor a PIN, the requirement’s
resolution_options says SET_PASSWORD. Use endpoint 9 only after verified phone
has produced the Imprint-issued setup grant; phone knowledge alone never grants
ownership of a pre-existing account.
A wrong credential is a 422. The message is identical whether or not an account
exists on that phone number — this endpoint is reachable with only a phone number,
so a distinguishable failure would make it an account-existence oracle. Do not try to
infer account existence from it.
Too many attempts returns the same 429 shape as endpoint 6, with
retry_after_seconds.
9. Establish authentication
PATCH — it gets its own audit trail and is never
replayable as form data.
password_policy before calling, so the applicant
gets an inline hint instead of a round-trip. The server enforces the same rule.
password is write-only and is never returned by any endpoint.
The authentication requirement blocks: COMPLETION, not SUBMIT — so you may
satisfy it any time before the application completes, including after an offer is
accepted. Doing it before accept means the accept call lands on COMPLETED
directly rather than parking in PENDING_AUTHENTICATION.
10. Submit for decisioning
poll_after_ms, or wait for
the application webhook — the status vocabulary
is identical between the two, so a polled value and a webhook value can be compared
directly.
blocking_requirements is the exact subset holding things up, in the same shape as
requirements — so you can route the applicant straight back to the right screen
without diffing anything. Remember that this includes PHONE_VERIFICATION
requirements, per the nesting rule above.
The outcomes
Six shapes come out of decisioning. A client that handles only approval handles a fraction of the contract.Reading an outcome
Readrecoverable and recovery_options to decide what the applicant can do — not
reason_code. A frozen credit file is recoverable by the applicant; an
underwriting decline generally is not. reason_code describes why and may change
independently of whether anything can be done about it.
reason_code and reasons[].code only ever grow as vocabularies, and codes are
never repurposed. Treat an unrecognized code as an unhandled default, not an
error.
outcome is scoped to the most recent decisioning round, not accumulated across all
of them. Resubmitting after recovering replaces the object wholesale — including
reasons — so you never have to work out which entries are stale. It is never set
back to null once populated: the last completed outcome remains visible while a
recovery decision round runs and is replaced atomically when the new round finishes.
Resubmit is permitted only when that latest outcome is explicitly recoverable and
its blockers have cleared. An ordinary repeated submit is a conflict unless it is an
idempotent replay.
Where a recovery step has a corresponding requirement, the same option appears in
both requirements[].resolution_options and outcome.recovery_options.
requirements is the authority on what to do; recovery_options is the
summary. Drive your form off requirements and use outcome to explain the
decision.
11. Start identity verification
IDV_REQUIRED. Returns a hosted URL to send the applicant to.
redirect_url must be allowlisted for your program; it is optional, and omitting it
lands the applicant on an Imprint completion page.
PENDING_DECISION and
decisioning resumes. A successful IDV is not itself an approval — poll or wait
for the webhook as you would after submitting. That is a second wait your client has
to handle.
The two 409s are separated because the right response differs.
IDV_ALREADY_COMPLETED is a race the applicant already won — stop asking and poll
for the decision. IDV_NOT_REQUIRED means you reached this endpoint without an
IDV_REQUIRED status, which is a dead end and a bug in your flow.
12. Accept an offer
OFFERS_AVAILABLE and creates the Imprint account.
You may pass card_design_id at the same time, which is equivalent to selecting it
via PATCH beforehand. Acceptance commits the effective selected/default design;
that value is then immutable and is the design used for card issuance.
OFFER_ACCEPTED while provisioning continues; customer_id
remains null. Poll the application. Replaying the same idempotency key returns the
same semantic acceptance result and never starts another account workflow.
customer_id is the existing external Imprint customer identifier used by the
servicing APIs, and is the handle for everything afterwards — the
servicing reads, transaction intents, rewards. Persist it
against your own customer record.
Note that ssn comes back as ssn_last_four. Sensitive values are returned masked
or omitted; the application is not a way to read back what was submitted.
If authentication is not yet established the application waits in
PENDING_AUTHENTICATION rather than COMPLETED — call endpoint 9 to finish. The
account exists either way and customer_id is populated.
A 409 means there are no offers available, an offer was already accepted, or the
offer expired. A 404 covers an offer_id that is not on this application.
Resolution options
resolution_options (on a requirement) and recovery_options (on an outcome) share
one shape. Their type names a route, and is a different vocabulary from the
credential type those routes accept.
Do not pass an option
type through as a request field. VERIFY_PASSWORD in a
body is rejected.
UNFREEZE_CREDIT has no endpoint: the applicant lifts the freeze with the bureau
directly, after which the application can be resubmitted.
resolution_options is present whenever an action needs to advertise which route
to use, even if only one is currently available. For account_ownership,
password-backed accounts verify a password, PIN-only accounts use hosted
authentication, and credential-less accounts set a password through an authorized
setup flow.
Statuses
ACCOUNT_FUNDED remains part of the shared application-webhook vocabulary for
funding products. This private-preview origination flow does not expose secured
funding or a funding operation, so it does not originate that transition.
Identical to the status on the
application webhook. The vocabulary grows over
time, so handle an unrecognized status as an unhandled default rather than an error.
Errors
Every error body carries atype and a message. type is what you branch on —
message is developer-facing English, subject to change, and not for showing to an
applicant.
Two statuses carry more than one
type with genuinely different handling: 404
(NO_PREFILL_MATCH is the applicant’s path forward, APPLICATION_NOT_FOUND is your
bug) and 409 (the two IDV conflicts above). Branching on status alone will get both
wrong.
For IDEMPOTENCY_IN_PROGRESS, wait the rounded-up retry_after_seconds (also in
Retry-After) and retry the identical request with the same key. For
IDEMPOTENCY_KEY_REUSED, do not retry that different request with the same key.
Card design selection during apply
A customer picking their card is part of the apply flow, not an afterthought: in the distinct-cards model the physical card is only issued once a design is selected (see Card issuance). If you render the application, you render the design picker, which means you need the design catalog. Two APIs cover this, and both are generally available today — unlike the rest of this page, they are published in the REST API reference and need no private-preview enablement.Retrieving the list of all card designs
customer_id controls whether tier-gated designs appear at all. Omit it and you
receive every design the program offers, each carrying its access_min_rank — the
minimum loyalty tier rank needed to select it, where 0 means available to everyone.
Supply it and designs above that customer’s tier are filtered out of the response
entirely, so every design you receive is one they can select.
This is the choice that determines what your picker can show:
If you want to render locked designs, call it without
customer_id and compare
access_min_rank against the customer’s tier yourself. Passing customer_id and
expecting to find locked designs in the response will not work — they are absent, not
flagged.
Only ACTIVE designs can be selected.
loyalty_tier_name is currently always omitted, so you cannot label a locked design
with the tier name it requires. Use access_min_rank and map it to your own tier
copy.starting_after on this endpoint takes the opaque next_page_token from the
previous response, not a card design id as other list endpoints do. Treat the
token as opaque — its contents may change.id as card_design_id on PATCH /v2/applications/{id}
or on accept. See
The card the customer is actually holding
for reading back the design that was actually printed on the issued card.
Rendering the artwork
light_asset_url_path and dark_asset_url_path are absolute URLs, served from
https://assets.imprint.co or https://app.imprint.co depending on where the
artwork was uploaded. Render them directly; do not prepend a host of your own.
Use image_orientation (VERTICAL or HORIZONTAL) to lay the artwork out — it
describes the artwork itself, so a horizontal asset rendered in a vertical frame will
look wrong. categories carries partner-defined key/value labels if you want to group
designs into sections in your picker.
Sequencing
The apply flow crosses the boundary described above: before the customer exists there is nocustomer_id to pass, and after they exist there is. Those two calls return
different result sets, so treat them as two separate fetches:
- Pre-signup teaser — call
GET /v2/card_designswith nocustomer_id. Every design, gated ones included. - The real picker — once the customer exists, call it again with
customer_id. Now scoped to what they can actually select.
customer_id is available.
A working sequence, end to end
Checklist before you go to production
The traps that cost the most time, in the order you will hit them:- Unknown
kind,key,status, andreason_codevalues are ignored, not thrown on. -
SUBMIT_BLOCKEDcounts includeblocks: PHONE_VERIFICATION, not justblocks: SUBMIT. - The published
password_policy.patterncompiles in your engine — with theuflag in JavaScript — and falls open if it does not. -
messagefields are read as locale dictionaries with anen-USfallback, not printed as objects. - A prefill
404branches ontype:NO_PREFILL_MATCHadvances the flow,APPLICATION_NOT_FOUNDis an error you log. - A prefill hit still collects the full nine-digit SSN.
- Resend is disabled for
resend_after_ms, and re-armed from the429’sretry_after_seconds. - Polling honours
poll_after_msrather than a fixed interval. -
NO_OFFERS_AVAILABLE,REJECTED,PENDING_REVIEW, andIDV_REQUIREDeach render something sensible — approval is one of six outcomes. - The IDV round-trip handles the second wait: finishing IDV returns to
PENDING_DECISION, not to an offer. -
adverse_action_notice_urlis never cached, logged, or persisted. -
Idempotency-Keyis set on create, phone verification, submit, and accept.