# Financing an invoice

Your identifiers are the keys here. `externalId` is whatever your own system already calls the
thing, and every write is idempotent on it, so the safest way to integrate is to replay your
ledger and let the API work out what changed.

Money crosses this boundary as integer cents everywhere. `100000` is $1,000.00.

## 1. Sync the business

```bash
curl -X PUT https://api.luxor.lunchpayments.com/v1/organizations/vendor-88 \
  -H "Authorization: Bearer lux_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "legalName": "Ridgeline Electrical LLC",
    "taxId": "12-3456789",
    "contact": { "email": "ap@ridgeline.example", "city": "Boise", "state": "ID" }
  }'
```

`legalName` is the only field you have to send. Include `taxId` when you hold one: it is how we
recognise a business that several platforms each serve as a separate customer, and compliance
cannot clear without it. We would rather leave it empty than guess. Any `contact` field you leave
out keeps whatever value it already had, so a partial update will not blank the rest.

## 2. Sync the invoice

An invoice identifier only has to be unique per payee rather than across your whole account. Two
of your customers may each number an invoice `001`, which is why the body names both
counterparties.

```bash
curl -X PUT https://api.luxor.lunchpayments.com/v1/invoices/INV-001 \
  -H "Authorization: Bearer lux_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "payee": { "externalId": "vendor-88", "legalName": "Ridgeline Electrical LLC" },
    "payor": { "externalId": "customer-12", "legalName": "Cedar Park Properties Inc" },
    "status": "ISSUED",
    "faceValue": 100000,
    "issueDate": "2026-09-01T00:00:00Z",
    "dueDate": "2026-11-30T00:00:00Z"
  }'
```

This call upserts both parties along with the invoice, so you do not have to do step 1 first.
Step 1 is how you go back and fill in a tax identification number or contact details you did not
have when the invoice first synced.

`status` is one of `ISSUED`, `PARTIALLY_PAID`, `PAID` or `VOIDED`, and `ISSUED` is the only one you
can request an advance against. Both dates are ISO date-times rather than plain dates.

If you are loading a backlog,
[`PUT /v1/invoices/imports/{externalId}`](/reference/invoices#import-a-spreadsheet-of-invoices) takes a
CSV of up to 1000 rows. Each row does exactly what the call above does, so you can fix three rows
in a rejected file and upload the whole thing again without disturbing the rest.

## 3. Quote it

A quote prices a face value for a business. It commits to nothing and creates nothing.

It does not name an invoice, since you pass the amount directly. That means you can quote before
step 2, or quote a figure a vendor is only considering. It does name a business, and that business
has to be synced already: an identifier we have not seen answers `404 business_not_found`.

```bash
curl -X POST https://api.luxor.lunchpayments.com/v1/quotes \
  -H "Authorization: Bearer lux_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "vendor-88", "invoiceFaceValue": 100000 }'
```

## 4. Take the advance

This call does name the invoice, and the payee along with it, because an invoice identifier is
only unique per payee:

```bash
curl -X POST https://api.luxor.lunchpayments.com/v1/advances \
  -H "Authorization: Bearer lux_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "payeeExternalId": "vendor-88", "invoiceExternalId": "INV-001" }'
```

It prices the advance again, checks that the business is eligible and within its limit, and then
creates it. It is idempotent on the invoice, so calling it twice will not advance twice.

Three refusals are worth handling by name, and all three come back as `409`:

- `invoice_not_financeable`: the invoice is not something we can advance against. Either its
  status is not `ISSUED`, or its face value is zero or negative (a credit note is not a
  receivable), or the payee and payor turned out to be the same business. A large invoice is not
  refused here: we cap what we will finance and advance against the capped figure, which comes
  back as `financeableAmount`.
- `business_not_eligible`: one of the three gates is still shut. Read
  [`GET /v1/organizations/{externalId}`](/reference/businesses#read-a-business-as-we-hold-it) to find out
  which one.
- `concentration_exceeded`: nothing is wrong with this invoice on its own, but too much of the
  business's outstanding exposure already sits with a single payer.

## 5. Then stop polling

Everything after this happens on its own clock rather than yours. The money moves, the payer
eventually settles, and the loan closes out, all long after your request returned. Rather than
polling for it, [subscribe to webhooks](/webhooks): `partner.loan.issued` tells you the vendor has
been paid, and `partner.invoice.paid` tells you the payer has settled.
