Yellowpay Partner API — Integration Guide

Sandbox endpoint: https://staging-api.getyellowpay.com/partner/graphql

GraphiQL: https://staging-api.getyellowpay.com/partner/graphiql

This guide covers the integration flow and the meaning behind each operation. For the complete list of types, fields and arguments, use the Docs panel in GraphiQL — it is generated from the live schema and always current.

What the API does

Yellowpay lets a business that buys from you take net terms (30, 60, 90 days…): Yellowpay funds the invoice to you up front, invoices the buyer with the agreed terms, and collects the repayment. The Partner API lets you offer this inside your own ordering flow: your customer picks "net terms" at checkout, your platform creates the request with Yellowpay, the customer e-signs a purchase agreement through DocuSign, and Yellowpay funds it.

Terminology

Yellowpay's terms may differ from the ones your platform uses. In this guide and in the API:

Term Meaning
Customer The business buying from you and repaying Yellowpay. If your platform calls these businesses something else — vendors, installers, dealers, accounts — this is them.
Vendor (or seller) You, the Yellowpay partner. Where the API says "vendor" (for example VENDOR_DELINQUENCY), it means you.
Terms request A request to finance a single invoice on net terms. One invoice, one terms request.
Net terms The number of days the customer has to repay, e.g. 30, 60, 90.
Credit limit The maximum a customer can have outstanding across all their terms requests with Yellowpay.
Invoice · funding · repayment The three money movements, always named distinctly. You invoice the customer. Yellowpay funds the invoice — the payout to you. The customer makes the repayment to Yellowpay. The API never uses a bare "payment" for any of these.

Environments

Sandbox Production
GraphQL endpoint https://staging-api.getyellowpay.com/partner/graphql Coming soon
GraphiQL https://staging-api.getyellowpay.com/partner/graphiql Coming soon

Production endpoints will be shared before your go-live; build and test against the sandbox for now.

The sandbox is fully separate from production: no real money moves and no real credit decisions are made. See Testing in the sandbox for what to expect from it.

Authentication

All requests must carry an API token issued by Yellowpay, sent as a bearer token in the Authorization header:

POST /partner/graphql HTTP/1.1
Host: staging-api.getyellowpay.com
Content-Type: application/json
Authorization: Bearer <your token>

Production tokens are created and managed by you in the Yellowpay app under Settings → API access (https://app.getyellowpay.com/settings/api-access). Sandbox tokens are issued by Yellowpay and sent to you by email — see Testing in the sandbox.

Each token has:

A request without a valid token fails with the error code UNAUTHENTICATED (see Errors).

To use GraphiQL, add the token in the Headers tab at the bottom of the editor:

{ "Authorization": "Bearer <your token>" }

Then verify your setup with tokenInfo — the simplest first call to make:

query TokenInfo {
  tokenInfo {
    name
    accessLevels
    expires
    ipAllowlist
  }
}

Conventions

Integration flow at a glance

Two stages. The first happens once per customer, the second once per order.

Integration flow: onboard the customer once, then request terms per order

1. Onboard a customer

Every read in the API is scoped to the customers linked to your account. A company enters your scope in exactly two ways: createCustomer (you register it with Yellowpay) or addCustomerByDuns (it's already registered — for example through another seller — and you link it). This section covers both.

1.1 Find the customer's DUNS

Onboarding keys on the DUNS number. If you don't have it, search by company name and (optionally) two-letter state code:

query LookupDuns($name: String!, $state: String) {
  lookupCompanyDuns(name: $name, state: $state) {
    duns
    name
    address
    isRegistered
  }
}
{ "name": "Sunrise Solar Installers", "state": "CA" }

The query returns a list of candidate matches. Company names are rarely unique, so present the candidates (name and address) to the customer and let them pick the right one rather than taking the first result. Including state narrows the results considerably.

isRegistered tells you which door to use next: true → link the customer with addCustomerByDuns; false → register them with createCustomer. Both are in 1.2.

Already registered with Yellowpay (isRegistered: true) — link the customer to your account:

mutation AddCustomerByDuns($duns: String!) {
  addCustomerByDuns(duns: $duns) {
    id
    businessName
    availableCredit
    creditApplication { status approvedLimit }
  }
}

It returns the full customer, credit standing included — if they already have an approved limit, you can skip straight to step 2. If no customer is registered with that DUNS, it fails with NOT_FOUND; register them instead. That fallback also makes a lookup-free shortcut possible when you already hold the DUNS: call addCustomerByDuns first, and on NOT_FOUND fall through to createCustomer.

Not yet registered (isRegistered: false) — register the customer:

mutation CreateCustomer($input: CustomerInput!) {
  createCustomer(input: $input) {
    id
    businessName
    duns
  }
}
{
  "input": {
    "businessName": "Sunrise Solar Installers LLC",
    "duns": "123456789",
    "address": {
      "streetAddress": "2100 Geng Road",
      "city": "Palo Alto",
      "state": "CA",
      "postalCode": "94304"
    },
    "primaryContact": {
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane@sunrisesolar.example",
      "phone": "650-555-0142"
    },
    "accountsPayable": {
      "name": "Bob Ledger",
      "email": "ap@sunrisesolar.example",
      "phone": "650-555-0143"
    }
  }
}

Note what's not here: no credit amount. Registration alone grants no credit; requesting a limit is the separate next step.

Field notes:

If a company with this DUNS is already registered, the mutation fails with DUPLICATE_DUNS and returns the existing customer in data — see Errors for how to make sure your client actually receives it. That customer isn't linked to your account yet: call addCustomerByDuns with the same DUNS, then continue with its id.

Store Customer.id. It is the customerId for every terms request this customer makes, and the argument to customer(id).

1.3 Request a credit limit

mutation RequestCreditLimit($customerId: Int!, $amount: Float!) {
  requestCreditLimit(customerId: $customerId, amount: $amount) {
    id
    availableCredit
    creditApplication {
      status
      requestedLimit
      approvedLimit
      created
    }
  }
}
{ "customerId": 4821, "amount": 50000.00 }

amount must be at least the invoice you're about to finance, and it caps every future terms request for this customer — so request what you expect the customer to need over time, not just today's order.

Handling creditApplication.status:

Status Meaning What to do
APPROVED The limit is granted; approvedLimit and availableCredit reflect it. Continue to step 2.
PENDING Yellowpay is underwriting the customer manually. Yellowpay contacts the customer directly, often asking for financials. Poll customer(id).creditApplication.status. You can create terms requests meanwhile, but they are held for manual review until the limit is approved — usually better to wait.
REJECTED Yellowpay declined to extend credit. Offer the customer another payment method.

requestCreditLimit is also how you request a higher limit for an already-approved customer. Calling it while an application is still PENDING fails with CREDIT_APPLICATION_PENDING and returns the customer (with the pending application) in data.

2. Request terms for an order

2.1 Price the options

One call prices every available net terms option, so you can render the whole selector — "Net 30 — $x · Net 60 — $y · Net 90 — $z" — from a single round trip:

query TermsCalculations($invoiceAmount: Float!, $termsFeePayer: TermsFeePayer) {
  termsCalculations(invoiceAmount: $invoiceAmount, termsFeePayer: $termsFeePayer) {
    netTerms
    invoiceAmount
    feesAmount
    totalAmount
  }
}
{ "invoiceAmount": 18750.00, "termsFeePayer": "CUSTOMER" }

Illustrative response (fee rates on your account will differ):

{
  "data": {
    "termsCalculations": [
      { "netTerms": 30, "invoiceAmount": 18985.00, "feesAmount": 235.00,  "totalAmount": 18985.00 },
      { "netTerms": 60, "invoiceAmount": 19218.75, "feesAmount": 468.75,  "totalAmount": 19218.75 },
      { "netTerms": 90, "invoiceAmount": 19453.00, "feesAmount": 703.00,  "totalAmount": 19453.00 }
    ]
  }
}

termsFeePayer decides who bears the cost of the terms. It defaults to SELLER when omitted.

termsFeePayer Who bears the fee invoiceAmount on the agreement totalAmount (customer repays) Your payout
CUSTOMER The customer — the fee is added to the invoice 19,218.75 (invoice + fee) 19,218.75 18,750.00
SELLER (default) You — the fee is deducted from your payout 18,750.00 18,750.00 18,281.25

Figures for the Net 60 row of the example. Re-run the query whenever the invoice amount or fee payer changes, and pass the same termsFeePayer to createTermsRequest, or the numbers the customer saw won't match the request.

2.2 Create the terms request

The invoice must exist before this call — Yellowpay finances a specific invoice, so issue it first and create the request against it.

Don't send this invoice to the customer yourself. Yellowpay invoices the customer directly, with the final amounts (including the fee when the customer bears it), the agreed terms, and the repayment details. Your invoice is the underlying document for the financing; if the customer received it too, they'd hold two invoices with different amounts and the wrong repayment instructions — and might repay you instead of Yellowpay.

mutation CreateTermsRequest($input: TermsRequestInput!) {
  createTermsRequest(input: $input) {
    id
    status
    amount
    netTerms
    termsFeePayer
    termsFeeAmount
    totalAmount
    purchaseDate
    dueDate
    reviewReasons
    signers {
      name
      email
      signing { status }
    }
  }
}
{
  "input": {
    "customerId": 4821,
    "amount": 18750.00,
    "netTerms": 60,
    "termsFeePayer": "CUSTOMER",
    "invoiceNumber": "INV-2026-00123",
    "invoiceFile": {
      "name": "INV-2026-00123.pdf",
      "base64": "<base64-encoded file>"
    },
    "poNumber": "PO-88412",
    "poFile": {
      "name": "PO-88412.pdf",
      "base64": "<base64-encoded file>"
    },
    "signers": [
      { "name": "Jane Doe", "email": "jane@sunrisesolar.example" }
    ],
    "signedAgreementCC": ["orders@yourcompany.example"]
  }
}

Field notes:

File size — the request body is capped at 25 MB in total, shared by everything in it. Base64 encoding adds about a third, so budget roughly 18 MB of original files per request, split between invoiceFile and poFile. A typical invoice PDF is well under 1 MB; the ceiling only matters for large scans.

What happens next depends on the customer's credit:

If DocuSign itself fails, the mutation returns DOCUSIGN_ERROR but the request is created — don't create it again; contact Yellowpay and the agreement will be sent.

Store the returned id; the response also carries the committed economics (termsFeeAmount, totalAmount, dueDate = purchaseDate + netTerms days), so you can render a confirmation without recalculating.

2.3 Track the request

Yellowpay's side of the lifecycle is asynchronous — signing, review, funding, repayment. There are no webhooks yet, so poll. A signing round trip takes hours to days; polling on the order of minutes is plenty.

Change polling — one query for "what changed since my last poll", instead of polling every open request individually:

query Changes($updatedSince: UTCDateTime!, $after: Cursor) {
  termsRequests(first: 50, after: $after, filters: { updatedSince: $updatedSince }) {
    edges {
      node {
        id
        status
        statusUpdated
        reviewReasons
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}

Pass the timestamp of your previous poll as updatedSince; page with after while hasNextPage is true. For one request in detail (per-signer progress, dates):

query TermsRequest($id: Int!) {
  termsRequest(id: $id) {
    id
    status
    reviewReasons
    dueDate
    signers {
      name
      email
      signing { status statusUpdated failureReason }
    }
  }
}
Status Meaning What to do
REQUESTED Submitted. If reviewReasons is non-empty, the request is held for manual review; the agreement is sent automatically once the review clears. Wait.
AGREEMENT_SENT The purchase agreement has been sent to the signers via DocuSign. Tell the customer to look for the DocuSign email. signers[].signing.status shows per-signer progress.
SIGNED All signers have signed. Yellowpay proceeds to funding.
FUNDED Yellowpay has funded the invoice — your payout has been made. The customer now owes the repayment to Yellowpay, not you.
REPAID The customer has repaid Yellowpay. Nothing — the cycle is complete.
REFUSED Yellowpay declined the request. Offer another payment method.
CANCELLED Cancelled — by the customer, or by you via cancelTermsRequest. Offer another payment method or close the order.
REFUND_REQUESTED The customer has requested a refund; Yellowpay handles this manually. No action for you.
UNCOLLECTIBLE Yellowpay could not collect the repayment. No action for you.

Per-signer signing.status values: SENT, RECEIVED, OPENED, SIGNED, DECLINED, FAILED. FAILED means the DocuSign email couldn't be delivered — check failureReason and the signer's address, then fix it with resendAgreement. DECLINED means the signer refused to sign.

2.4 Fix problems: resend or cancel

Agreement bounced or wrong signer — resend, optionally with a corrected signer list (a supplied list replaces the current signers; omit it to resend to the current ones):

mutation ResendAgreement($id: Int!, $signers: [SignerInput!]) {
  resendAgreement(id: $id, signers: $signers) {
    id
    status
    signers { name email signing { status } }
  }
}

Allowed only while the status is AGREEMENT_SENT — after the agreement has gone out and before it's signed; otherwise it fails with REQUEST_STATUS_VIOLATION.

Order fell through — cancel:

mutation CancelTermsRequest($id: Int!) {
  cancelTermsRequest(id: $id) {
    id
    status
  }
}

Allowed while the status is REQUESTED, AGREEMENT_SENT or SIGNED — i.e. any time before funding. After FUNDED, contact Yellowpay instead.

3. Checking a customer's credit

Before offering net terms on an order, check whether the customer can take it:

query CustomerCredit($id: Int!) {
  customer(id: $id) {
    id
    businessName
    availableCredit
    creditApplication { status approvedLimit }
  }
}

customers(first: 20, filters: { query: "sunrise" }) lists your customers as a paginated connection, with free-text search.

Errors

Errors follow the GraphQL convention: errors[] carries the details, with a machine-readable extensions.code and an extensions.status mirroring the equivalent HTTP status.

{
  "data": { "customers": null },
  "errors": [
    {
      "message": "You need to log in to perform this action",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["customers"],
      "extensions": { "code": "UNAUTHENTICATED", "status": 401, "reason": null }
    }
  ]
}
Code Returned by Meaning and recovery
UNAUTHENTICATED any operation Token missing, invalid, expired, or the request came from outside the IP allowlist.
DUPLICATE_DUNS createCustomer A customer with this DUNS already exists. The existing customer is returned in data — link it to your account with addCustomerByDuns, then continue with its id.
NOT_FOUND addCustomerByDuns No customer is registered with this DUNS — register it with createCustomer instead.
DUPLICATE_INVOICE_NUMBER createTermsRequest A request with this invoice number already exists. The existing request is returned in data — this makes retries after timeouts safe.
CREDIT_APPLICATION_PENDING requestCreditLimit An application is already under review. The customer (with the pending application) is returned in data.
REQUEST_STATUS_VIOLATION cancelTermsRequest, resendAgreement The request's status doesn't allow this operation — see the allowed statuses in section 2.4.
DOCUSIGN_ERROR createTermsRequest, resendAgreement The DocuSign envelope could not be sent. The request still exists — on create, contact Yellowpay to get the agreement sent; on resend, try again.

One failure bypasses this format entirely: a request body over 25 MB is rejected at the transport level with a plain HTTP 413 — no errors array, no extensions.code. If your client reports a parse failure or a bare 413 on createTermsRequest, check the size of the attached files first (see File size).

⚠ Configure your client for error-plus-data responses

The three DUPLICATE_* / *_PENDING errors return the existing object alongside the error — that's what makes them recoverable in one step. But many GraphQL clients discard data when errors is present. Apollo Client's default errorPolicy: 'none' does exactly this — with the default configuration, the customer or request Yellowpay hands back silently vanishes.

// Apollo Client — required for these mutations
const [createCustomer] = useMutation(CREATE_CUSTOMER, {
  errorPolicy: 'all', // keep data AND errors
});

Whatever your client, verify it surfaces both data and errors for these three mutations. Then branch on extensions.code, never on message text.

Operation reference

Operation Purpose Access level
tokenInfo Verify your token and its access levels READ
lookupCompanyDuns(name, state) Find a company's DUNS by name; flags already-registered companies READ
createCustomer(input) Register a customer (no credit yet) CREATE_CUSTOMER
addCustomerByDuns(duns) Link an already-registered customer to your account CREATE_CUSTOMER
requestCreditLimit(customerId, amount) Apply for (or raise) a customer's credit limit CREATE_CUSTOMER
customer(id) Fetch a customer, their available credit and credit application READ
customers(first, after, filters) List / search your customers (paginated) READ
netTermsOptions Available net terms, in days READ
termsCalculations(invoiceAmount, termsFeePayer) Price all net terms options in one call READ
createTermsRequest(input) Create a terms request for an invoice CREATE_TERMS_REQUEST
termsRequest(id) Fetch one terms request with signer detail READ
termsRequests(first, after, filters) List / filter your terms requests; updatedSince for change polling READ
resendAgreement(id, signers) Resend the DocuSign agreement, optionally with new signers CREATE_TERMS_REQUEST
cancelTermsRequest(id) Cancel a request before funding CREATE_TERMS_REQUEST

Full field-level documentation for every type is in the Docs panel in GraphiQL.

Testing in the sandbox

The sandbox runs the same API against test data. Current behaviour:

Go-live checklist

Changelog