Developers

Build on the same API our own screens are built on

This is the integration reference, written from the integrator's chair. OneApp Finance is designed API-first: a public REST surface, OAuth2, and signed webhooks back both its own borrower and operations screens and yours, with no private backdoors. What follows is the contract you build against, not a "call it live today" pitch. Where /modularity/ covers who renders each step, this page is the wire-level shape: the endpoints, the auth, the events, and the guarantees the Core is built to hold.

The API is the product surface, not an afterthought.

OneApp Finance is software, not a lender. The platform is designed so there is exactly one engine underneath: the same public API drives the white-label screens we ship and any front end you build. Nothing your integration can reach is a private side channel, and nothing our own screens use is hidden from you. That is the point of an API-first design: the contract is the system, so what you build against is what actually runs.

API-first overview

A public REST surface, designed as a contract

An inbound integration submits a complete application for a first- or second-look decision over the public REST API, the same API OneApp Finance's own screens are built on. The shape below is the designed contract: a typed request, an explicit idempotency key, and a 202 that hands the decision back on a signed webhook. Build against this; nothing here claims a live service you can call today.

One endpoint

Submit an application, get a decision

POST the full application, including the consent evidence, and the Core returns a 202 while it works. The decision arrives on the decision.completed webhook, or inline in sync mode. A single decisioned status carries the approve, decline, refer, or counteroffer outcome. Idempotent on your key, so a retry is safe.

POST /partner-intake/applications
// The contract you build against, not "call it live today".
// Base URL is your own instance host (single-tenant).
POST https://{instance-host}/partner-intake/applications
Authorization: Bearer <oauth2-client-credentials-token>
Idempotency-Key: "7c3f1a90-2b6e-4d1c-9a2f-001"
Content-Type: application/json

{
  "look_type": "FIRST_LOOK",
  "partner_external_ref": "ACME-APP-99812",
  "program_id": "prg_01J...",
  "borrower": { "...": "..." },
  "property": { "...": "..." },
  "requested_amount": { "currency": "USD", "amount_cents": 1850000 },
  "consent": {
    "soft_or_hard": "HARD_PULL",
    "copy_deck_version": "acme_consent_v4",
    "render_hash_sha256": "sha256:..."
  }
}

// 202 Accepted
{
  "application_id": "app_01J...",
  "status": "submitted",
  "decision": null
}

// Decision arrives on decision.completed (or ?mode=sync).
// One decisioned status carries the outcome on decision_outcome.
Auth & environments

OAuth2 client credentials, against your own host

Authentication is designed as OAuth2 client credentials: your service exchanges a client ID and secret for a short-lived bearer token, then carries that token on every call. There is no shared platform gateway and no global tenant. The base URL is your own instance host, because each operator runs a single-tenant install. Your token, your host, your data, never pooled across operators.

  • Client credentials grant. Machine-to-machine. You hold the secret, rotate it on your schedule, and scope each client to the programs it may act on.
  • Your instance is the base URL. https://{instance-host} resolves to your single-tenant deployment. There is no multi-tenant endpoint to address by accident.
  • Scoped tokens. A token is designed to carry only the program and capability scopes you granted the client, so an intake client cannot reach a funding action it was never given.
  • Sandbox first. The intended path is to build and certify against a sandbox instance with the same contract, then point the same code at production by changing the host and credentials.

How auth resolves

  • OAuth2 client credentials, short-lived bearer tokens
  • Base URL is your own single-tenant instance host
  • Per-client scopes by program and capability
  • One contract across sandbox and production
Events & webhooks

Subscribe once. The Core tells you what happened.

Every state change you care about is designed to arrive as a signed webhook. Deliveries are retried with backoff until your endpoint returns a 2xx, each carries an event ID so a duplicate is safe to ignore, and you verify the signature against your secret before you trust the body. These are the same events the Core is built to emit, to its own screens and to yours.

  • decision.completed The approve, decline, refer, or counteroffer outcome is ready on the application.
  • application.status_changed The application moved between states: submitted, decisioned, signed, funded, or withdrawn.
  • document.completed An e-sign or disclosure packet finished, with the evidence package attached.
  • disbursement.released A staged draw cleared after the homeowner confirmed it on their own device.
  • dealer.standing_changed A contractor's standing state changed and may gate their new applications.
decision.completed -> your endpoint
// The delivery contract you build against, not "call it live today".
// Signed delivery to the URL you register.
// Verify the signature before you trust the body.
POST https://{your-endpoint}
OneApp-Signature: t=1714000000,v1=<hmac-sha256>
OneApp-Event-Id: "evt_01J..."  // dedupe; retried until 2xx
Content-Type: application/json

{
  "event": "decision.completed",
  "application_id": "app_01J...",
  "partner_external_ref": "ACME-APP-99812",
  "decision_outcome": "APPROVE",  // APPROVE | DECLINE | REFER | COUNTEROFFER
  "occurred_at": "..."
}
Idempotency & errors

Retries are safe. Some failures are meant to stay failures.

A distributed integration retries. The contract is designed so that retrying is the correct default, and so that the errors which protect a borrower or the ledger do not quietly disappear on the next attempt. Two ideas do the work: idempotency keys, and typed errors that say plainly whether you should try again.

idempotency

One key, one effect

Every state-changing call carries an Idempotency-Key you generate. Send the same key twice and the Core is designed to return the original result rather than act a second time. A dropped connection, a timeout, a redeploy mid-call: retry with the same key and you get exactly-once behavior, never a double submission and never a double disbursement.

typed errors

Errors that tell you what to do

Failures are designed to be typed, not a bare status code. Each error carries a stable code and a retryable flag, so your client knows whether to back off and try again or to stop and fix the request. Transient infrastructure errors are retryable; a malformed or unauthorized request is not.

invariants

Some errors are non-retryable by design

A class of errors represents a structural invariant, not a glitch. Try to fund without a consent artifact and the Core is built to return a non-retryable, non-overridable error: hammering the endpoint will never change the answer, because the missing input is the point. The fix is the artifact, not the retry.

Versioning & pinning

Every decision pins the exact version that ran

An audit asks what ran, not what runs today. So each decision is designed to bind the exact model version and policy version that produced it, plus a hashed snapshot of the inputs. A replay loads that pinned version and reproduces the original outcome. It never resolves "latest," because "latest" is whatever changed since, and that is exactly what a regulator does not want to see substituted for the record.

  • Decisions are immutable. The model and policy version that decided an application are written into the decision record and do not move when you ship a new version.
  • Replay is reproducible. Re-run a past decision and the Core loads the pinned version against the frozen inputs, so you get the same answer years later.
  • A change is a new version, never an edit. Tweak a cutoff and you publish a new version. Old decisions keep pointing at the version that was in force when they ran.
  • API contracts are explicit. The request and response shapes are designed to be versioned deliberately, so an integration you certify keeps working against the contract it was built on.

What pinning buys you

  • Each decision binds its exact model and policy version
  • A hashed input snapshot, frozen at decision time
  • Replay loads that version, never "latest"
  • A new cutoff is a new version, not an edit in place

The full reference comes with onboarding.

An honest note: the endpoints, event names, and field shapes on this page are the designed contract, drawn to show you the integration model, not a published spec you can hit right now. The complete reference, the sandbox instance, and your scoped credentials are provisioned during onboarding, against your own single-tenant host. For who renders each step, how to embed by SDK or iframe, and where ownership sits per step, see Modularity

Straight answers

Common questions

Can I call the API today?

Not from this page. What you see here is the designed contract: the endpoints, auth model, events, and guarantees the system is built around. The live sandbox, the full reference, and your scoped OAuth2 credentials are provisioned during onboarding, against your own instance host.

Is this the same API your own screens use?

Yes, by design. OneApp Finance is API-first: the same public REST surface and signed webhooks back both the white-label screens we ship and any front end you build. There are no private backdoors, so the contract you build against is the system that actually runs.

If I retry a call, will I double-submit or double-fund?

No, that is what idempotency keys are for. Every state-changing call carries a key you generate, and resending the same key is designed to return the original result rather than act again. Errors are typed with a retryable flag, and structural failures, like funding without a consent artifact, are non-retryable on purpose: the answer will not change until the missing input exists.
Build on it

See the contract running on your own instance

Walk the intake call, the signed events, and version-pinned replay against a sandbox, then map your build with our team.