Advanta / Banks and funders / Developers

Advanta funder API

Your own system reads the funding requests Advanta routes to you, sends your decisions, sets what you require before you fund, manages your limits, sends your bank statement for matching, invites your own clients and suppliers, and receives signed webhooks when something happens. Every write has the same effect as the same action in the funder portal. And it takes all your data, whenever you want: your exit right.

  • OpenAPI 3.1
  • Version 2026-09-23
  • JSON over HTTPS

Quick start

One base URL serves live and sandbox keys alike: the key decides what you see.

Base URL
https://bzsengbmlzzhztjfvcag.supabase.co/functions/v1/funder-api
Authentication
Authorization: Bearer adv_fk_… on every call: adv_fk_ followed by 64 hexadecimal characters.
Format
JSON in and out. A bank statement can also be sent as the raw XML or CSV file.

Start with GET /v1/me: it says which funder the key belongs to, its environment and its scopes. Then list the requests routed to you.

curl
export ADVANTA_KEY="adv_fk_…"   # your key, shown once when it was issued
export BASE="https://bzsengbmlzzhztjfvcag.supabase.co/functions/v1/funder-api"

# 1. Who am I: funder, environment, scopes
curl -s "$BASE/v1/me" \
  -H "Authorization: Bearer $ADVANTA_KEY"

# 2. The requests routed to me that are still open
curl -s "$BASE/v1/requests?status=open&limit=20" \
  -H "Authorization: Bearer $ADVANTA_KEY"
JavaScript
// Node.js 18 or later, as an ES module (fetch and top-level await are built in)
const BASE = 'https://bzsengbmlzzhztjfvcag.supabase.co/functions/v1/funder-api';
const headers = { Authorization: `Bearer ${process.env.ADVANTA_KEY}` };

async function call(path) {
  const res = await fetch(BASE + path, { headers });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
  return body;
}

const me = await call('/v1/me');
console.log(me.environment, me.funder.name, me.key.scopes);

const { count, requests } = await call('/v1/requests?status=open&limit=20');
console.log(`${count} open request(s)`);
for (const r of requests) {
  console.log(r.request_ref, r.status, r.total_amount, r.my_offer?.status ?? 'no offer yet');
}

GET /v1/me answers like this:

Response · GET /v1/me
{
  "api_version": "2026-09-23",
  "environment": "sandbox",
  "funder": {
    "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3",
    "name": "Compartment Demo (LU)"
  },
  "key": {
    "id": "0b3c6a52-5d0e-4b53-9d0e-1c2b3a4d5e6f",
    "name": "sandbox · Example prospect",
    "prefix": "adv_fk_1a2b3",
    "scopes": ["requests:read", "limits:read", "webhooks:write"],
    "created_at": "2026-09-23T18:00:00Z",
    "expires_at": "2026-10-07T18:00:00Z",
    "prospect": "Example prospect"
  }
}

Environments and sandbox

There is one base URL. The key decides the environment, and GET /v1/me returns it in environment.

environmentThe funderWhat the key sees
liveClassified by Advanta as real or pilot.Live customers only.
sandboxA demonstration funder.Fictitious companies on the demonstration plane only.
unclassifiedNot classified by Advanta yet.No request at all.

A request on the other plane is never visible, whatever the key.

Sandbox keys

  • Advanta issues a sandbox key for a named prospect (key.prospect in GET /v1/me). Like every key, it is shown once and only its SHA-256 is stored.
  • It expires 1 to 90 days after it is issued (key.expires_at); from then on every call answers 401 key_expired. Advanta can also revoke it at any time.
  • It sees fictitious companies on the demonstration plane only. That data is shared with other demonstrations and rebuilt when the scenario is reset, so expect it to change.
  • It reads requests, settings, limits, the payer record and your clients (prescribers, links, invitations and the report), and manages its own webhook endpoints and onboarding links: it sees only the ones it created. A supplier invited with a sandbox key is registered as a test company on the demonstration plane.
  • It can make decisions (offer, decline, fund, settle), add prescribers and issue links only if Advanta enabled that on the key, which adds the decisions:write scope. The demonstration funder's prescribers and links are shared by every demonstration.

You can have a sandbox key today: your own system against our demonstration data, before any contract. Write to diogo@advanta.pt. A live key is issued the day you want to connect.

Authentication and scopes

Every call carries Authorization: Bearer adv_fk_<64 hex>. Keys are issued by Advanta operations and belong to one funder. A key is shown once, only its SHA-256 is stored, and it may expire.

Each key carries scopes. A call the key has no scope for answers 403 forbidden.

ScopeWhat it allows
requests:readReading requests (GET /v1/requests, GET /v1/requests/{id} and its documents). It also reads your settings and required documents, the limit books, the payer record, your clients (prescribers, links, invitations and the report), your onboarding links, your webhook endpoints and their delivery logs. It also reads your programme terms (GET /v1/programme), the recourse claims (GET /v1/recourse), your bank data (GET /v1/bank-data), and a request's notices and the cash flow its seller shares with you.
limits:readReading the limit books (GET /v1/credit-limits, GET /v1/seller-limits) and the payer record.
limits:writeSetting, revising or refusing limits (PUT /v1/credit-limits, PUT /v1/seller-limits).
decisions:writeOffering, declining, funding and settling (POST /v1/requests/{id}/decision), and raising what the debtors of a request must sign (POST /v1/requests/{id}/consent-level). It also allows every write on settings, limits, statements, clients, onboarding links and webhook endpoints, and reading webhook endpoints, clients and onboarding links. It also allows exporting all your data. It also announces a confidential request (POST /v1/requests/{id}/announce), closes a recourse claim (POST /v1/recourse/{id}/close) and changes your programme terms.
settings:writeChanging what your debtors must sign before you fund (PUT /v1/settings) and the documents you require from sellers (PUT /v1/required-documents). Also your programme terms (PUT /v1/programme).
statements:writeSending a bank statement (POST /v1/statements).
clients:writeManaging your clients: adding and switching off prescribers (POST /v1/prescribers, POST /v1/prescribers/{id}/disable), issuing, replacing and switching off links (POST /v1/client-links, POST /v1/client-links/{id}/disable), and reading them, the invitations and the report.
webhooks:writeManaging webhook endpoints: create, stop, rotate the secret, send a test, and read the list and the delivery logs.
onboarding:writeCreating onboarding links for your suppliers (POST /v1/onboarding-links) and reading them.
exports:writeExporting all your data (POST /v1/exports, GET /v1/exports, GET /v1/exports/{id} and its documents). Live keys only.

Endpoints

Paths are relative to the base URL. Every call can answer 401 (unauthorized or key_expired), a call that needs a scope can answer 403 forbidden (the key lacks the scope, or the funder is not active), and 500 means retry later. These are not repeated below; Errors lists every code.

Your key

Which funder a key belongs to, its environment and its scopes. Start here.

GET/v1/health

Whether the API is up. Public: no key, no data, no database call.

Scope
None: public, no key
Returns
200{ status, api_version, time }
Errors
None

Advanta's own checks read it every 5 minutes; the results are on the public status page.

curl
curl -s "$BASE/v1/health"

GET/v1/me

Who is calling: the key, its funder, its environment and its scopes.

Scope
Any valid key
Returns
200{ api_version, environment, funder, key }
Errors
405method_not_allowed

key.prefix is the first 12 characters of the key. key.expires_at is null for a key that does not expire; key.prospect names who a sandbox key was issued to.

curl
curl -s "$BASE/v1/me" \
  -H "Authorization: Bearer $ADVANTA_KEY"
Response · 200
{
  "api_version": "2026-09-23",
  "environment": "sandbox",
  "funder": {
    "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3",
    "name": "Compartment Demo (LU)"
  },
  "key": {
    "id": "0b3c6a52-5d0e-4b53-9d0e-1c2b3a4d5e6f",
    "name": "sandbox · Example prospect",
    "prefix": "adv_fk_1a2b3",
    "scopes": ["requests:read", "limits:read", "webhooks:write"],
    "created_at": "2026-09-23T18:00:00Z",
    "expires_at": "2026-10-07T18:00:00Z",
    "prospect": "Example prospect"
  }
}

Requests and decisions

Read the requests Advanta routed to you, then offer, decline, fund or record money received; and ask more of a request's debtors when you need to.

GET/v1/requests

The requests routed to you (you can act on them) and those you already acted on (read only, even after a re-routing), newest first, each with your own offer in my_offer if you made one.

NameInDescription
statusqueryopen (the default: pending, submitted, under_review, terms_sent, accepted, approved), all, or a single status.
limitquery1 to 200, default 50.
Scope
requests:read
Returns
200{ funder, count, requests[] }
Errors
Only the common ones (401, 403, 500).

A request carries id, request_ref, type (factoring or confirming), status, total_amount, advance_amount, the seller in company_name and company_nif, invoice_count, assigned_bank_id, consent_level_required (what every debtor must sign before it can be funded, set when it was routed), its timestamps and, per debtor, debtor_consent. The spec lists every field.

curl
curl -s "$BASE/v1/requests?status=all&limit=50" \
  -H "Authorization: Bearer $ADVANTA_KEY"
JavaScript
const BASE = 'https://bzsengbmlzzhztjfvcag.supabase.co/functions/v1/funder-api';
const headers = { Authorization: `Bearer ${process.env.ADVANTA_KEY}` };

const res = await fetch(`${BASE}/v1/requests?status=open&limit=200`, { headers });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { funder, requests } = await res.json();

// Routed to you (you can act on them) and not priced by you yet
const toPrice = requests.filter((r) => r.assigned_bank_id === funder.bank_id && !r.my_offer);
for (const r of toPrice) console.log(r.request_ref, r.type, r.total_amount, r.company_name);
Response, abridged · 200
{
  "funder": {
    "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3",
    "name": "Compartment Demo (LU)"
  },
  "count": 1,
  "requests": [
    {
      "id": "4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87",
      "request_ref": "FR-2026-0042",
      "type": "factoring",
      "status": "terms_sent",
      "total_amount": 18450.00,
      "company_name": "Example Seller, S.L.",
      "invoice_count": 3,
      "assigned_bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3",
      "consent_level_required": "acknowledgment",
      "created_at": "2026-09-22T09:12:00Z",
      "my_offer": {
        "funding_request_id": "4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87",
        "rate": 2.4,
        "advance_pct": 90,
        "days": 60,
        "status": "pending",
        "submitted_at": "2026-09-23T10:30:00Z",
        "accepted_at": null
      },
      "debtor_consent": [
        {
          "debtor_name": "Example Buyer, S.A.",
          "debtor_tax_id": "A00000000",
          "status": "pending",
          "level_required": "acknowledgment",
          "level_achieved": null,
          "meets_requirement": false
        }
      ]
    }
  ]
}

GET/v1/requests/{id}

The underwriting packet of one request: the request, its receivables, the seller's latest Advanta Score, your own offer and risk, the fraud signals and disputes on the request as your portal shows them.

NameInDescription
id requiredpathThe request id.
Scope
requests:read
Returns
200{ request, debtor_consent[], receivables[], score, my_offer, risk }
Errors
404not_found

score runs from 300 to 850, with a grade from A to E and its dimensions. It is computed on the spot when the seller has none yet, and is null if that fails. Each receivable has invoice_no, invoice_amount, invoice_date, invoice_due_date and the debtor in customer_name. debtor_consent gives each debtor's notice status, the level required and the level signed (level_achieved), meets_requirement, the channel, the declaration text signed and who signed it.

risk.signals lists the fraud signals still open or confirmed, each with a stable code (for example amount_above_history) and a severity. A hard one (for example an amount changed while the invoice is in a request, a credit note against it or an open dispute) stops routing and funding until Advanta's operations team reviews it; the others only flag. risk.disputes lists every dispute recorded against the request.

curl
curl -s "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87" \
  -H "Authorization: Bearer $ADVANTA_KEY"

POST/v1/requests/{id}/decision

Offer terms, decline, fund, or record a settlement. The request must be routed to you, else 409 not_routed_to_you.

NameInDescription
id requiredpathThe request id.

The body takes one of four shapes, chosen by decision:

decisionFieldsEffect
offerrate (percent, above 0 and up to 100) and advance_pct (same range), both required; days, notes, valid_days (1 to 90).Sends your terms and moves a pending, submitted or under-review request to terms_sent. The offer expires after valid_days, else after your configured validity (7 days by default). Refused with 409 credit_limit when one of your limits cannot hold it, with 409 debtor_not_eligible_for_funder when you take listed buyers only and a debtor is not on your list, and with 409 documents_missing_for_funder when a document you require from the seller is missing, unverified or expired.
declinereason or notes, at least one; notes is the message the seller reads. internal_note optional: kept for your team, never shown to the seller.Closes the request. reason is one of payer_credit, invoice_ineligible, documentation, concentration, amount_out_of_range, sector_excluded, no_funder_capacity, withdrawn_by_client or other; free text is kept as a note with reason other.
fundvalue_date (YYYY-MM-DD, the value date of your transfer to the seller, at most 7 days ahead) and reference (your transfer's reference, 3 to 140 characters); amount optional, checked against the accepted advance.Only once the seller accepted your terms (status approved). For factoring, every debtor's notice must be acknowledged first (else 409 funding_gate_failed), and the documents you require from the seller must be valid on the funding date (else 409 documents_missing_for_funder).
settleamount (required); received_at (defaults to now, at most one day ahead); reference (up to 140 characters); note (up to 1,000); final.Records money received on a funded request. Partial payments add up; the request becomes settled when the total reaches the request amount or final is true.
Scope
decisions:write
Returns
201{ decision, request_id, offer_id, expires_at, status } (offer)
200{ decision, request_id, status } (decline)
200{ decision, request_id, status, funded_at } (fund)
200{ decision, request_id, settlement_id, status, received_total, request_total, outstanding } (settle)
Errors
400reason_required
403not_your_request (settle only: you are not the funder of record)
404not_found
409not_routed_to_you, invalid_state, credit_limit, funding_gate_failed, funding_amount_mismatch, debtor_not_eligible_for_funder, documents_missing_for_funder
422invalid_terms, invalid_amount, invalid_received_at, invalid_funding_details, step_back_message_required, invalid_decision
curl
curl -s -X POST "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/decision" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision":"offer","rate":2.4,"advance_pct":90,"days":60,"valid_days":7,"notes":"Subject to the notice being acknowledged"}'
JavaScript
async function offer(requestId, terms) {
  const res = await fetch(`${BASE}/v1/requests/${requestId}/decision`, {
    method: 'POST',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({ decision: 'offer', ...terms }),
  });
  const body = await res.json();
  if (res.status === 201) return body; // { decision, request_id, offer_id, expires_at, status }
  const { code, message, debtors } = body.error;
  if (code === 'debtor_not_eligible_for_funder') {
    for (const d of debtors) console.warn(d.name ?? d.debtor_key, d.reason, d.invoices);
  }
  throw new Error(`${res.status} ${code}: ${message}`);
}

await offer('4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87', { rate: 2.4, advance_pct: 90, days: 60, valid_days: 7 });
Response · 201
{
  "decision": "offer",
  "request_id": "4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87",
  "offer_id": "7a9e3c1b-2d4f-4e6a-8b0c-5d1e2f3a4b5c",
  "expires_at": "2026-09-30T10:30:00Z",
  "status": "terms_sent"
}
Bodies for decline, fund and settle
{ "decision": "decline", "reason": "concentration", "notes": "We cannot fund this buyer this quarter", "internal_note": "Group exposure above our internal limit" }

{ "decision": "fund", "value_date": "2026-10-01", "reference": "TRF-2026-1001-07" }

{ "decision": "settle", "amount": 12500.00, "received_at": "2026-10-30T10:00:00Z", "reference": "ADV-A1F51301-1000" }

Documents and rules

What must be in place before you fund: the documents you require from sellers, per seller country, and what each debtor must sign. Each request keeps the versions that satisfied you.

GET/v1/requests/{id}/documents

The deal file of one request. When the request was routed to you, and again when it was funded, Advanta recorded which version of each document you require satisfied you: its file name, SHA-256, issue and validity dates, and when and how Advanta verified it. That record never changes, even when the seller uploads newer versions; each entry also says where the document stands today, and current lists the seller's current documents.

NameInDescription
id requiredpathThe request id.
Scope
requests:read
Returns
200{ request_id, seller_country, today, required_now, snapshots[], current[] }
Errors
404not_found

Every entry with a file carries a download_url valid for two minutes (url_expires_in), and each URL issued is logged. Compare the SHA-256 of what you download with the one recorded.

curl
curl -s "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/documents" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/required-documents

The documents you require from sellers, per seller country, and the catalogue they are chosen from: each document's code, its name in seven languages, issuer, usual validity and sensitivity. The usual validity only pre-fills: the date printed on the document is what the seller records and Advanta checks.

Scope
requests:read
Returns
200{ funder, required, catalogue[] }
Errors
Only the common ones (401, 403, 500).

A request is routed to you, your offer is recorded and the request is funded only while every document on your list for the seller's country is verified by Advanta and valid on that date; otherwise 409 documents_missing_for_funder.

curl
curl -s "$BASE/v1/required-documents" \
  -H "Authorization: Bearer $ADVANTA_KEY"

PUT/v1/required-documents

Replace your list for one seller country; an empty list clears it. Codes come from the catalogue, and the change is kept on Advanta's evidence chain with note. Requests already with you stay with you; your next offer or funding on one of them waits until the seller meets the new list (open_requests_not_meeting counts them).

NameInDescription
country requiredbodyThe seller country, two letters.
codes requiredbodyDocument codes from the catalogue.
notebodyUp to 500 characters.
Scope
settings:write or decisions:write
Returns
200{ …required documents, changed, country, codes, open_requests_not_meeting }
Errors
422body_required, country_invalid, codes_invalid, note_invalid, country_without_catalogue, document_code_unknown
curl
curl -s -X PUT "$BASE/v1/required-documents" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"country":"PT","codes":["cert_hacienda","cert_seguridad_social","cirbe"],"note":"Credit committee of 23 September"}'

GET/v1/settings

What your debtors must sign before you fund: the level every debtor must sign before a factoring request routed to you can be funded (receipt, then acknowledgment, then acceptance), whether only counsel-approved declaration texts count, your open requests per level, and which levels each governing law has declaration texts for, in which languages.

Scope
requests:read
Returns
200{ funder, debtor_consent_level, require_counsel_approved_texts, open_requests_by_level, levels, levels_by_law, texts_counsel_approved }
Errors
Only the common ones (401, 403, 500).
curl
curl -s "$BASE/v1/settings" \
  -H "Authorization: Bearer $ADVANTA_KEY"

PUT/v1/settings

Change what your debtors must sign. The change applies to requests routed to you from then on; a request already routed keeps the level it was routed with. reason is kept on Advanta's evidence chain.

NameInDescription
debtor_consent_levelbodyreceipt, acknowledgment or acceptance.
require_counsel_approved_textsbodytrue to count only counsel-approved declaration texts.
reason requiredbodyWhy, at least 8 characters.
Scope
settings:write or decisions:write
Returns
200{ …settings, changed, open_requests_keep_their_level }
Errors
422level_invalid, invalid_flag, reason_required
curl
curl -s -X PUT "$BASE/v1/settings" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"debtor_consent_level":"acknowledgment","reason":"Credit committee decision of 22 September"}'

Programme terms and recourse

Choose, as your default and per company, whether each debtor is notified before you fund (announced) or the notice is prepared and kept ready (confidential), with or without recourse, and whether you ask for the seller's bank data. Then announce a confidential request, read its notices and the cash flow its seller shares with you, and follow your recourse claims.

GET/v1/programme

Your programme terms: your default, the terms you set for particular companies, the companies you can set terms for (those with a request routed to you, and your own clients), and your factoring requests counted by the terms they carry. A request takes the terms in force when it is routed to you and keeps them.

Scope
requests:read
Returns
200{ funder, bank_id, default, companies[], sellers[], requests }
Errors
Only the common ones (401, 403, 500).

disclosure: announced (the default): each debtor is notified and acknowledges the assignment before you fund. confidential: when the request is routed to you, the notice to each debtor is prepared, fingerprinted (SHA-256) and stored ready to send, and funding does not wait for the debtor. Before funding, every blocking invoice check must pass, the invoices must come from the seller's own books (ERP, e-invoice or SAF-T), every debtor must have a current prepared notice naming you and an address to send it to, and the seller's verification must be approved. The notices are sent when you announce the request, and automatically on a funded request when a trigger you turned on is met: announce_overdue_days, announce_on_dispute, announce_on_kyc_lapse.

recourse: with or without. recourse_days: days after an invoice's due date before a funded request not yet settled opens a claim (null: none). bank_data: off, requested (the seller is asked to connect its business bank account for you, read-only and with its consent) or required (funding also waits until the account you pay into is confirmed as the seller's own).

curl
curl -s "$BASE/v1/programme" \
  -H "Authorization: Bearer $ADVANTA_KEY"
Response, abridged · 200
{
  "funder": { "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3", "name": "Example Funder" },
  "default": {
    "disclosure": "announced",
    "recourse": "with",
    "recourse_days": 30,
    "announce_overdue_days": null,
    "announce_on_dispute": false,
    "announce_on_kyc_lapse": false,
    "bank_data": "requested",
    "source": "funder_default",
    "updated_at": "2026-09-28T10:00:00Z"
  },
  "companies": [
    {
      "seller_id": "2f6d8a4e-1b3c-4d5e-9f70-8a1b2c3d4e5f",
      "company_name": "Example Seller, S.L.",
      "tax_id": "B00000000",
      "terms": { "disclosure": "confidential", "recourse": "with", "recourse_days": 30, "announce_overdue_days": 15, "announce_on_dispute": true, "announce_on_kyc_lapse": true, "bank_data": "required", "source": "seller" },
      "reason": "Credit committee of 28 September"
    }
  ],
  "requests": { "announced:with:open": 4, "confidential:with:funded": 1 }
}

PUT/v1/programme

Change your default (no seller_id) or one company's terms. Only the keys you send change; {"reset": true} with a seller_id returns that company to your default. The change applies to requests routed to you from then on; requests already routed keep their terms. reason is kept on Advanta's evidence chain.

NameInDescription
seller_idbodyA company's id from sellers in GET /v1/programme. Absent or null: your default.
terms requiredbodyThe terms to change: disclosure, recourse, recourse_days, announce_overdue_days, announce_on_dispute, announce_on_kyc_lapse, bank_data; or reset.
reason requiredbodyWhy, at least 8 characters.
Scope
settings:write or decisions:write
Returns
200{ funder, bank_id, seller_id, terms, changed, open_requests_keep_their_terms }
Errors
422body_invalid, seller_id_invalid, terms_invalid, terms_key_unknown, reason_required, seller_not_in_programme
curl
curl -s -X PUT "$BASE/v1/programme" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"seller_id":"2f6d8a4e-1b3c-4d5e-9f70-8a1b2c3d4e5f","terms":{"disclosure":"confidential","announce_overdue_days":15,"announce_on_dispute":true,"announce_on_kyc_lapse":true,"bank_data":"required"},"reason":"Credit committee of 28 September"}'

POST/v1/requests/{id}/announce

Announce a confidential request routed to you: it becomes announced and the notices prepared for it are sent to its debtors, who then acknowledge them as in an announced programme. Possible while the request is pending, under review, with terms sent, approved or funded. A request.announced event follows; once a notice has been sent the request stays announced.

NameInDescription
id requiredpathThe request id.
reason requiredbodyWhy, at least 8 characters. Kept on Advanta's evidence chain.
Scope
decisions:write
Returns
200{ request_id, disclosure, trigger, notices, sending_queued }
Errors
403not_routed_to_you
404not_found
422body_invalid, reason_required, already_announced, request_closed
curl
curl -s -X POST "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/announce" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Invoice 2026-118 is 20 days overdue"}'

GET/v1/requests/{id}/notices

The request's notices of assignment, one per debtor, each with its prepared document when there is one: the exact subject and HTML the debtor receives, its language, governing law and SHA-256. For a request routed to you, or one you acted on.

NameInDescription
id requiredpathThe request id.
Scope
requests:read
Returns
200{ request_id, count, notices[] }
Errors
404not_found

The HTML carries a placeholder, ACK_LINK in double braces, where the debtor's acknowledgment link goes; the link holds a secret and is never shown. current says the document still matches the notice's invoices and amount.

curl
curl -s "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/notices" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/requests/{id}/cash-flow

What the seller shares with you from its business bank account, read-only and with its consent, through a licensed account-information provider: the connection, its accounts (the last four characters of each IBAN) and their balances, twelve months of movements by month, and the check of its payout account. cash_flow is null while the seller has not connected an account for you.

NameInDescription
id requiredpathThe request id.
Scope
requests:read
Returns
200{ request_id, bank_data, cash_flow, bank_data_problems[], payment_alerts[] }
Errors
404not_found

bank_data_problems says what funding still waits for when your terms require bank data. payment_alerts are the payments the seller received from a debtor of this request after you funded it under confidential terms. A seller's consent is given to one funder: you see only what it shares with you.

curl
curl -s "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/cash-flow" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/recourse

Your funded factoring requests by recourse type (with, without, and not_recorded for those routed before 28 September 2026), the claims counted by kind and status, and the claims, open ones first. A claim opens once per request, when a funded request is not settled recourse_days after an invoice's due date: recourse, against the seller, for the amount advanced plus your fees, less what you already collected; debtor_default, without recourse, for your own collections and insurance.

NameInDescription
statusqueryopen, settled, withdrawn or written_off. Absent: every claim.
Scope
requests:read
Returns
200{ funder, bank_id, currency, as_of, by_type[], claims[], list[] }
Errors
422status_invalid

The seller repays a recourse claim into pay_to_iban quoting pay_reference. When the payments you record from then on (a statement line you confirm, or the settle decision) cover claim_amount, the claim is settled and the request closes as settled by recourse: the invoices go back to the seller, still unpaid by the debtor.

curl
curl -s "$BASE/v1/recourse?status=open" \
  -H "Authorization: Bearer $ADVANTA_KEY"

POST/v1/recourse/{id}/close

Close an open claim of yours: a recourse claim is withdrawn (waived, or agreed otherwise with the seller), a debtor default is written_off (passed to your collections or your insurer). reason is kept on Advanta's evidence chain.

NameInDescription
id requiredpathThe claim id (claim_id).
status requiredbodywithdrawn or written_off.
reason requiredbodyWhy, at least 8 characters.
Scope
decisions:write
Returns
200{ claim_id, status }
Errors
403not_your_claim
404not_found
422body_invalid, status_invalid, reason_required, close_invalid, claim_closed
curl
curl -s -X POST "$BASE/v1/recourse/$CLAIM_ID/close" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"withdrawn","reason":"Payment plan agreed with the seller on 2 October"}'

GET/v1/bank-data

Your own collection account, connected by your administrator in the portal and read the same way: its movements go through the same matching as POST /v1/statements, so collections match your funded requests without uploading files. Also how many sellers share their account with you, the payment alerts on your requests (the last 180 days) and the provider's charges recorded against you, by month.

Scope
requests:read
Returns
200{ funder, bank_id, own_accounts[], sellers_sharing, payment_alerts[], charges[] }
Errors
Only the common ones (401, 403, 500).

unit_price_eur and amount_eur are null until the provider's price is fixed.

curl
curl -s "$BASE/v1/bank-data" \
  -H "Authorization: Bearer $ADVANTA_KEY"

Limits and payer record

Your exposure per debtor and per seller, applied by the platform itself, with limits set one by one or up to 2,000 at once; how a buyer pays its suppliers; and what Advanta knows about each debtor on a request.

GET/v1/credit-limits

Your book per debtor: your limit and what is outstanding, committed, offered and still available, in euros.

Scope
limits:read or requests:read
Returns
200{ funder, currency, count, debtors[] }
Errors
Only the common ones (401, 403, 500).

Each debtor carries debtor_key, debtor_name, debtor_country, limit_status, limit_amount, outstanding, committed, offered, available, overdue_count, oldest_overdue, frozen, decided_at and note.

curl
curl -s "$BASE/v1/credit-limits" \
  -H "Authorization: Bearer $ADVANTA_KEY"

PUT/v1/credit-limits

Set or revise your limit on a debtor, or refuse the debtor. The database refuses any later offer this limit cannot hold.

NameInDescription
debtor_tax_id requiredbodyThe debtor's VAT or tax number.
debtor_countrybodyISO 3166 alpha-2.
debtor_namebodyThe debtor's name.
amountbodyEuros, 0 or more. Required for an active limit.
statusbodyactive (the default), refused or suspended.
notebodyFree text.
Scope
limits:write or decisions:write
Returns
200{ credit_limit }
Errors
422debtor_tax_id_required, invalid_amount, invalid_limit
curl
curl -s -X PUT "$BASE/v1/credit-limits" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"debtor_tax_id":"A00000000","debtor_country":"ES","debtor_name":"Example Buyer, S.A.","amount":250000,"status":"active"}'

POST/v1/credit-limits/import

Load up to 2,000 limits on debtors in one call: your own book, or your credit insurer's limits. Each row is checked exactly as PUT /v1/credit-limits checks one and is answered on its own: a row that fails does not stop the others, and a debtor that appears twice in one call is refused the second time.

NameInDescription
rows requiredbody1 to 2,000 rows, each with debtor_tax_id and, as in PUT /v1/credit-limits, debtor_country, debtor_name, amount, status and note. amount is a number or digits with a decimal point; it is required for an active limit. A list of rows alone, without the object around it, is also accepted (as source import).
sourcebodyimport (your own limits, the default) or insurer (your credit insurer's limits).
source_refbodyThe file or batch the limits come from, at most 200 characters.
Scope
limits:write or decisions:write
Returns
200{ funder, source, source_ref, received, written, refused, rows[] }
Errors
413too_many_rows
422rows_required, invalid_source, source_ref_invalid, source_ref_too_long

Every limit written records its source and reference, on the limit and on its history. The answer counts received, written and refused, and answers each row with row (counted from 1), debtor_tax_id and ok: a written row adds debtor_key, status and amount; a refused row gives its error (invalid_amount, debtor_tax_id_required, duplicate_debtor, invalid_status, amount_required, invalid_country, row_invalid or invalid_limit) and may add a detail.

curl
curl -s -X POST "$BASE/v1/credit-limits/import" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source":"insurer","source_ref":"Insurer limits 2026-09","rows":[{"debtor_tax_id":"A00000000","debtor_country":"ES","debtor_name":"Example Buyer, S.A.","amount":250000},{"debtor_tax_id":"500000000","debtor_country":"PT","status":"refused"}]}'

GET/v1/seller-limits

Your book per seller, with the same figures as the debtor book.

Scope
limits:read or requests:read
Returns
200{ funder, currency, count, sellers[] }
Errors
Only the common ones (401, 403, 500).

Each seller carries seller_id, seller_name, seller_nif, seller_country and the same limit and exposure fields as a debtor.

curl
curl -s "$BASE/v1/seller-limits" \
  -H "Authorization: Bearer $ADVANTA_KEY"

PUT/v1/seller-limits

Set or revise your limit on a seller, or refuse the seller.

NameInDescription
seller_id requiredbodyAs returned by GET /v1/seller-limits.
amountbodyEuros, 0 or more.
statusbodyactive (the default), refused or suspended.
notebodyFree text.
Scope
limits:write or decisions:write
Returns
200{ seller_limit }
Errors
404not_found
422seller_id_required, invalid_amount, invalid_limit
curl
curl -s -X PUT "$BASE/v1/seller-limits" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"seller_id":"2f6d8a4e-1b3c-4d5e-9f70-8a1b2c3d4e5f","amount":100000,"status":"active","note":"Annual review"}'

GET/v1/payer-record

How a buyer pays its suppliers, as aggregates only, from the ledgers of the suppliers connected to Advanta. The record is published when at least three suppliers observe the buyer; otherwise status says why and every figure is null. You can read it only for a buyer on a request in your scope or on your debtor list; a sandbox key reads the demonstration plane's record.

NameInDescription
tax_id requiredqueryThe buyer's VAT or tax number.
countryqueryISO 3166 alpha-2.
Scope
limits:read or requests:read
Returns
200{ funder, payer_record }
Errors
404not_found (the buyer is on no request in your scope and not on your debtor list)
422tax_id_required

Figures include observers, invoice_count, avg_days_to_pay, median_days_to_pay, on_time_pct, the settlement delay (avg_settle_delay_days, median_settle_delay_days, p75_settle_delay_days, worst_settle_delay_days), open and overdue counts, window_from and refreshed_at. payer_record is null when there is no record.

curl
curl -s "$BASE/v1/payer-record?tax_id=A00000000&country=ES" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/requests/{id}/debtors

What Advanta knows about each debtor on a factoring request routed to you, or one you acted on: its level on Advanta since level_since (notice: it knows Advanta from the notice only; account: it claimed its payer account; books: it connected its accounting system; buyer: it runs a buyer programme, and buyer_programme is true), the seller's own history with it, its official payment period when it is a public body, and your own limit on it.

NameInDescription
id requiredpathThe request id.
Scope
requests:read
Returns
200{ funder, request_id, count, debtors[] }
Errors
404not_found

score is the debtor's own score (grade, 0 to 100, when and by which model version). It is given only when the debtor shares its data with the funders that finance invoices owed by it, is at level books or buyer, and the request is routed to you (not one you only acted on); otherwise it is null. pair_history is the seller's history with this debtor over 36 months, with the payer record's rules: real invoices only, and lateness only against due dates stated on the document or in the ERP. public_payment_period is the latest official average payment period of a public body (DGAL for Portuguese municipalities, Hacienda for Spanish local entities). credit_limit is your limit on the debtor, where it came from (source, source_ref) and what is used (exposure: outstanding plus committed plus offered, in euros).

curl
curl -s "$BASE/v1/requests/4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87/debtors" \
  -H "Authorization: Bearer $ADVANTA_KEY"

Statements and settlements

Send the statement of your collection account and each credit is matched to your funded requests; you confirm each match in the portal. To record a payment yourself, send the settle decision.

POST/v1/statements

Send the statement of your collection account for matching: a CAMT.053 (XML) or CSV file, at most 4 MiB and 5,000 transactions. Each credit is matched against your own funded requests, first on the reference the debtor quoted. Nothing is settled until you confirm a match in the portal. The same file twice is refused, by its SHA-256.

NameInDescription
file_namequeryThe file name (optional).
kindquerycamt053 or csv; detected when omitted.
the file requiredbodyThe raw file, sent as application/xml or text/csv; or JSON { file_name, kind, content_base64 }.
Scope
statements:write or decisions:write
Returns
201{ import_id, file_sha256, lines, credits, duplicates, proposed, suggested, unmatched, ignored, … }
Errors
409statement_already_imported
413file_too_large, too_many_lines
422file_required, file_unreadable, file_empty, statement_unreadable, statement_has_no_transactions, line_invalid
curl
curl -s -X POST "$BASE/v1/statements?file_name=statement-2026-10-30.xml&kind=camt053" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @statement-2026-10-30.xml

Clients and prescribers

Bring your own clients: an open link of your own, one link per prescriber, and the invitations and the report that show who came through each. The companies you bring are routed only to you.

GET/v1/prescribers

Your prescribers, the people and firms that bring you clients (accountants, advisers, brokers), active first, each with its current link and the number of companies that arrived through it. They are yours, not Advanta's partners. The same list as the Clients page of your portal.

NameInDescription
langqueryThe language of the page each link opens: pt, es, en, fr, de, it or nl, added to its url.
Scope
requests:read, clients:write or decisions:write
Returns
200{ funder, count, prescribers[] }
Errors
422lang_invalid

A sandbox key reads the demonstration funder's prescribers, which every demonstration shares.

curl
curl -s "$BASE/v1/prescribers?lang=es" \
  -H "Authorization: Bearer $ADVANTA_KEY"

POST/v1/prescribers

Add a prescriber. Its own open link is issued with it, in prescriber.link.url. Its name and e-mail stay off Advanta's evidence chain: only their hashes go on it.

NameInDescription
name requiredbody2 to 120 characters, unique among your active prescribers.
emailbodyOptional. A sandbox funder records only reserved addresses (ending in .example, .invalid, .test or .demo).
notesbodyOptional, up to 1,000 characters.
langbodyThe language of the page its link opens, added to its url.
Scope
clients:write or decisions:write
Returns
201{ prescriber }
Errors
409prescriber_exists
422body_required, name_required, name_invalid, email_invalid, email_not_reserved, notes_too_long, lang_invalid

With a sandbox key this changes shared demonstration state, so the key needs decisions:write.

curl
curl -s -X POST "$BASE/v1/prescribers" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Gestoría Norte","email":"ana@gestoria-norte.example","notes":"Accountant in Bilbao","lang":"es"}'
Response, abridged · 201
{
  "prescriber": {
    "id": "5f1c2a8e-3b7d-4e0a-9c61-2d4b8e7f9a10",
    "name": "Gestoría Norte",
    "email": "ana@gestoria-norte.example",
    "notes": "Accountant in Bilbao",
    "active": true,
    "created_at": "2026-09-10T09:00:00Z",
    "disabled_at": null,
    "link": {
      "id": "1e2f3a4b-5c6d-4e7f-8a9b-0c1d2e3f4a5b",
      "prescriber_id": "5f1c2a8e-3b7d-4e0a-9c61-2d4b8e7f9a10",
      "state": "active",
      "expires_at": null,
      "arrivals": 0,
      "url": "https://app.advanta.capital/join?link=…&lang=es"
    }
  }
}

POST/v1/prescribers/{id}/disable

Switch a prescriber off. Its link stops working at once; the companies it brought stay attributed to it. Switching off a prescriber that is already off changes nothing (changed is false). Recorded on Advanta's evidence chain.

NameInDescription
id requiredpathThe prescriber id.
reasonbodyWhy, up to 200 characters.
Scope
clients:write or decisions:write
Returns
200{ prescriber, changed }
Errors
404not_found
405method_not_allowed
422body_invalid, reason_invalid
curl
curl -s -X POST "$BASE/v1/prescribers/$PRESCRIBER_ID/disable" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Agreement ended"}'

GET/v1/prescribers/report

What each prescriber brought you: one group per prescriber, then your own link (own_link), your invitations by name (named) and your onboarding links (api). Per group and per company: when it arrived, when its account was opened (joined), when its company verification was approved (onboarded), the requests routed to you, the amount requested and the amount funded. The same numbers as your portal.

Scope
requests:read, clients:write or decisions:write
Returns
200{ funder, plane, generated_at, currency, totals, groups[] }
Errors
405method_not_allowed

A company counts once, for whoever brought it to you first. The amount funded is the advance (else the total) of funded or settled requests, in euros; totals adds up every group. Only your own data and only companies on your data plane: a sandbox key does not see what other sandbox keys made through the API.

curl
curl -s "$BASE/v1/prescribers/report" \
  -H "Authorization: Bearer $ADVANTA_KEY"
Response, abridged · 200
{
  "funder": {
    "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3",
    "name": "Compartment Demo (LU)"
  },
  "plane": "sandbox",
  "generated_at": "2026-09-26T10:00:00Z",
  "currency": "EUR",
  "totals": { "arrived": 1, "joined": 1, "onboarded": 1, "requests": 1, "amount_requested": 18450.00, "amount_funded": 16605.00 },
  "groups": [
    {
      "kind": "prescriber",
      "prescriber_id": "5f1c2a8e-3b7d-4e0a-9c61-2d4b8e7f9a10",
      "name": "Gestoría Norte",
      "active": true,
      "since": "2026-09-10T09:00:00Z",
      "arrived": 1,
      "joined": 1,
      "onboarded": 1,
      "requests": 1,
      "amount_requested": 18450.00,
      "amount_funded": 16605.00,
      "companies": [
        {
          "company_name": "Example Seller, S.L.",
          "company_tax_id": "B00000000",
          "country": "ES",
          "arrived_at": "2026-09-15T11:02:00Z",
          "joined_at": "2026-09-15T11:20:00Z",
          "onboarded_at": "2026-09-16T09:15:00Z",
          "requests": 1,
          "amount_requested": 18450.00,
          "amount_funded": 16605.00
        }
      ]
    }
  ]
}

GET/v1/client-invitations

Every invitation of yours, newest first: through a link, by name in the portal, or with an onboarding link. Each says how the company came (via: link, named or api), the prescriber that brought it if any, its state (pending, accepted, revoked or expired), the company, the contact and the dates.

NameInDescription
limitquery1 to 500, default 200.
Scope
requests:read, clients:write or decisions:write
Returns
200{ funder, count, invitations[] }
Errors
405method_not_allowed

Only your own data plane. A sandbox key does not see the invitations other sandbox keys made.

curl
curl -s "$BASE/v1/client-invitations?limit=50" \
  -H "Authorization: Bearer $ADVANTA_KEY"

Opportunities of the day

Every morning, the invoices of your own clients that became eligible under your rules overnight, and what changed in those you were shown. The opportunities.daily webhook says when there is something to read.

GET/v1/opportunities

The digest of a day, from your own clients (the companies whose home funder you are) that allow you to see their invoices. Every morning at about 06:30 Madrid time, after the nightly prequalification, Advanta lists the invoices that became eligible under your rules since the previous digest (kind new) and what changed in those you were shown before: paid, disputed (the client recorded that its debtor contests it) or no_longer_eligible.

NameInDescription
datequeryThe day of the digest, YYYY-MM-DD (Madrid time). Absent: the latest. A day without a digest answers date null and no lines.
Scope
requests:read
Returns
200{ funder, date, digest, lines[] }
Errors
404not_found
405method_not_allowed
422invalid_date

A line carries the invoice, its debtor with its tax id, the amount still owed, the due date, and the client with its sector (NACE section), grade and size band; new lines come first, then the changes. no_longer_eligible has a reason: requested (the client asked to finance it), cancelled, removed (gone from the client's books), left_programme, not_shared (the client stopped sharing) or rules (it no longer meets your rules).

digest holds the totals: how many clients share and how many do not yet, what is eligible today, what is new and what changed. stale is true when the night's run did not complete: nothing was compared that morning. A digest is a list the database computed, not an offer: a request is checked again when it is made, and you decide on it as always. The opportunities.daily webhook says when a digest with something in it is ready.

curl
curl -s "$BASE/v1/opportunities?date=2026-10-07" \
  -H "Authorization: Bearer $ADVANTA_KEY"
Response, abridged · 200
{
  "funder": { "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3", "name": "Example Funder" },
  "date": "2026-10-07",
  "digest": {
    "digest_id": "9b2d4c1e-0f3a-4e5b-8c6d-7a8b9c0d1e2f",
    "date": "2026-10-07",
    "stale": false,
    "snapshot_at": "2026-10-07T03:30:12Z",
    "clients": { "sharing": 9, "not_sharing": 1 },
    "eligible": { "count": 42, "total": 318450.00 },
    "new": { "count": 6, "total": 41200.00 },
    "changes": { "paid": 2, "disputed": 0, "no_longer_eligible": 1 },
    "currency": "EUR",
    "lines": "/v1/opportunities?date=2026-10-07"
  },
  "lines": [
    {
      "kind": "new",
      "reason": null,
      "invoice_id": "0c5e7f3a-2b4d-4c6e-9f81-3a5b7c9d1e2f",
      "number": "FV 2026/1184",
      "debtor": { "name": "Example Buyer, S.A.", "tax_id": "A00000000", "country": "ES" },
      "amount": 12400.00,
      "currency": "EUR",
      "due_date": "2026-12-15",
      "client": { "seller_id": "2f6d8a4e-1b3c-4d5e-9f70-8a1b2c3d4e5f", "name": "Example Seller, S.L.", "tax_id": "B00000000", "sector": "C", "grade": "B", "size_band": "small" }
    }
  ]
}

Webhook endpoints

Register the endpoints that receive webhooks, rotate their secrets, send a test and read the delivery log.

GET/v1/webhooks

Your active endpoints, without their secrets, and the event types you can subscribe to. A sandbox key sees only the endpoints it created.

Scope
webhooks:write, decisions:write or requests:read
Returns
200{ count, webhooks[], event_types[] }
Errors
Only the common ones (401, 403, 500).
curl
curl -s "$BASE/v1/webhooks" \
  -H "Authorization: Bearer $ADVANTA_KEY"

POST/v1/webhooks

Register an endpoint. The response holds its signing secret, which is not shown again.

NameInDescription
url requiredbodyhttps on a public host name, port 443, with no user name or password; at most 2,048 characters. Private, loopback and link-local addresses are refused, and redirects are never followed.
eventsbodyA list of event types, or ["*"] for all of them (the default).
descriptionbodyUp to 200 characters.
Scope
webhooks:write or decisions:write
Returns
201{ webhook, secret, signature }
Errors
409too_many_endpoints
422body_required, url_required, url_invalid, url_too_long, url_must_be_https, url_has_credentials, url_port_not_allowed, url_host_not_allowed, url_not_allowed, events_invalid, unknown_event_type, invalid_description

At most 10 active endpoints.

curl
curl -s -X POST "$BASE/v1/webhooks" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/advanta","events":["request.routed","notice.acknowledged","notice.disputed","request.funded","settlement.recorded"],"description":"Credit desk"}'
JavaScript
const res = await fetch(`${BASE}/v1/webhooks`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://hooks.example.com/advanta',
    events: ['request.routed', 'notice.acknowledged', 'notice.disputed', 'request.funded', 'settlement.recorded'],
    description: 'Credit desk',
  }),
});
const body = await res.json();
if (res.status !== 201) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);

// The secret is shown once: store it now, in your own secret store.
await secretStore.put(`advanta-webhook-${body.webhook.id}`, body.secret);
Response, abridged · 201
{
  "webhook": {
    "id": "e3b1c2d4-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "url": "https://hooks.example.com/advanta",
    "description": "Credit desk",
    "events": ["notice.acknowledged", "notice.disputed", "request.funded", "request.routed", "settlement.recorded"],
    "active": true,
    "secret_prefix": "whsec_1a2b3c",
    "created_via": "api"
  },
  "secret": "whsec_…",
  "signature": { "header": "Advanta-Signature", "scheme": "HMAC-SHA256 of \"<t>.<raw body>\"", "tolerance_seconds": 300 }
}

DELETE/v1/webhooks/{id}

Stop an endpoint. Deliveries still pending are cancelled and its secrets are deleted.

NameInDescription
id requiredpathThe endpoint id.
Scope
webhooks:write or decisions:write
Returns
200{ id, active: false, deleted: true, cancelled_deliveries }
Errors
404not_found
curl
curl -s -X DELETE "$BASE/v1/webhooks/$WEBHOOK_ID" \
  -H "Authorization: Bearer $ADVANTA_KEY"

POST/v1/webhooks/{id}/rotate-secret

A new signing secret, shown once. The previous secret keeps signing, as a second v1 in the header, for keep_previous_hours, so you can switch without losing events.

NameInDescription
id requiredpathThe endpoint id.
keep_previous_hoursbody0 to 72, default 24.
Scope
webhooks:write or decisions:write
Returns
200{ webhook, secret, previous_secret_valid_until }
Errors
404not_found
422invalid_keep_previous_hours
curl
curl -s -X POST "$BASE/v1/webhooks/$WEBHOOK_ID/rotate-secret" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"keep_previous_hours":24}'

POST/v1/webhooks/{id}/test

Queue a webhook.test event to this endpoint. The next dispatcher run delivers it, within about a minute.

NameInDescription
id requiredpathThe endpoint id.
Scope
webhooks:write or decisions:write
Returns
202{ endpoint_id, event_id, delivery_id, status: "pending" }
Errors
404not_found
curl
curl -s -X POST "$BASE/v1/webhooks/$WEBHOOK_ID/test" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/webhooks/{id}/deliveries

The delivery log of an endpoint, newest first: status (pending, delivered, failed or cancelled), attempts, response code, last error, next attempt, and the SHA-256 of the exact body sent.

NameInDescription
id requiredpathThe endpoint id.
limitquery1 to 200, default 50.
Scope
webhooks:write, decisions:write or requests:read
Returns
200{ count, deliveries[] }
Errors
404not_found
curl
curl -s "$BASE/v1/webhooks/$WEBHOOK_ID/deliveries?limit=20" \
  -H "Authorization: Bearer $ADVANTA_KEY"

Onboarding links

From your own product, hand a supplier a single-use Advanta onboarding link; the supplier comes back to you with a signed status when it is done.

Your data export

Take everything you can see on Advanta, whenever you want, without asking: your exit right (DORA Art. 28(8) and Art. 30(3)(f)). A ZIP with one CSV and one JSON Lines file per entity, schema.json, manifest.json with every file's SHA-256, a README in your language and verify.py.

POST/v1/exports

Start an export of everything your funder can see: requests and their history, offers, invoices with their verification, sellers (company data, the KYC summary you may see, the representative's verification), debtors, notices with the debtors' answers and the certificates' data, the evidence chain entries about your operations with the hashes and day roots to verify them, settlements and statements, gate refusals, your rules and limits, your team, webhook endpoints (never a secret), API keys (never a key), your audit trail and your documents. It is built in the background, usually within a minute; export.ready tells you when.

NameInDescription
langbodyThe language of the README: pt, es, en, fr, de, it or nl (default en).
Scope
exports:write or decisions:write
Returns
202{ funder, export }
Errors
403export_live_key_required
409export_in_progress
422body_invalid, lang_invalid
429export_rate_limited

A live key only: a sandbox key answers 403 export_live_key_required (the demonstration funder is shared). One export at a time, at most 10 in 24 hours. The same export is on the portal's Export page, under settings.

curl
curl -s -X POST "$BASE/v1/exports" \
  -H "Authorization: Bearer $ADVANTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lang":"es"}'
Response · 202
{
  "funder": { "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3", "name": "Example Funder" },
  "export": {
    "id": "5e1f0a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
    "subject": "funder",
    "status": "queued",
    "plane": "live",
    "requested_via": "api",
    "actor_role": "api:treasury",
    "lang": "es",
    "created_at": "2026-09-27T09:00:02.114000Z",
    "download_available": false,
    "manifest_sha256": null,
    "progress": { "entities_done": 0, "entities_total": 47, "rows": 0, "volumes_written": 0, "phase": "data" }
  }
}

GET/v1/exports

Your exports, from the portal and the API, newest first, with their status, size and manifest_sha256.

NameInDescription
limitqueryFrom 1 to 200, 50 by default.
Scope
exports:write or decisions:write
Returns
200{ funder, count, exports[] }
Errors
403export_live_key_required
405method_not_allowed
curl
curl -s "$BASE/v1/exports?limit=20" \
  -H "Authorization: Bearer $ADVANTA_KEY"

GET/v1/exports/{id}

One export. While it is built: status queued or running, with progress. Once ready, and for 24 hours: files, each volume with a link valid for one hour at most (never past those 24 hours). Ask again for fresh links; every call that returns links is recorded. After 24 hours the files are deleted and the record stays.

NameInDescription
id requiredpathThe export id, from POST /v1/exports or GET /v1/exports.
Scope
exports:write or decisions:write
Returns
200{ funder, export, files[] }
Errors
403export_live_key_required
404not_found
405method_not_allowed

manifest_sha256 is the SHA-256 of manifest.json written without that field, keys sorted, no spaces. Advanta recorded the same value on the evidence chain (entry export.ready); verify.py recomputes it and checks every file and the evidence entries.

curl
curl -s "$BASE/v1/exports/5e1f0a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer $ADVANTA_KEY"
Response, abridged · 200
{
  "funder": { "bank_id": "6d164c92-02cc-4f9a-ae8d-3f7f5ef043d3", "name": "Example Funder" },
  "export": {
    "id": "5e1f0a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
    "status": "ready",
    "completed_at": "2026-09-27T09:00:41.502000Z",
    "expires_at": "2026-09-28T09:00:41.502000Z",
    "download_available": true,
    "manifest_sha256": "1c822b8797ba58903eca0689cc0c93138e7d396e01a038d133b5c1f3aeec7497",
    "size_bytes": 482113,
    "file_count": 101,
    "documents_mode": "files",
    "row_counts": { "requests": 212, "invoices": 1480, "evidence_entries": 903 }
  },
  "files": [
    {
      "name": "advanta-export-2026-09-27-5e1f0a2b-v001.zip",
      "bytes": 482113,
      "sha256": "9b0e5c1d2a7f4e6b8c3d1a0f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a291807",
      "url": "https://…/object/sign/data-exports/…",
      "url_expires_at": "2026-09-27T10:00:41.502000Z"
    }
  ]
}
Download and check · shell
# Every volume into one folder, then the checker the export carries
curl -s "$BASE/v1/exports/$EXPORT_ID" -H "Authorization: Bearer $ADVANTA_KEY" \
  | jq -r '.files[].url' | while read -r u; do curl -s -o "v$((++n)).zip" "$u"; done
for z in v*.zip; do unzip -q -o "$z" -d export; done
python3 export/advanta-export-*/verify.py   # files, manifest digest and evidence chain

GET/v1/exports/{id}/documents

When the documents were too large to include (documents_mode links): every document of the export with a link valid for 15 minutes, a page at a time. The list is computed when you ask, so a document you may no longer read is not in it.

NameInDescription
id requiredpathThe export id.
afterqueryThe previous page's next_after.
limitqueryFrom 1 to 200, 100 by default.
Scope
exports:write or decisions:write
Returns
200{ funder, export_id, documents[], next_after }
Errors
403export_live_key_required
404not_found
409export_not_ready, export_failed
410export_expired
curl
curl -s "$BASE/v1/exports/5e1f0a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b/documents?limit=100" \
  -H "Authorization: Bearer $ADVANTA_KEY"

Webhooks

Register an HTTPS endpoint and Advanta sends it a signed POST when something happens that concerns you: on a request routed to you or its seller, or on an onboarding link you created. Each endpoint subscribes to the event types it needs. Each delivery is signed, and retried for 24 hours if it fails.

Events

EventWhendata
request.routedAdvanta operations routed a request to you. You can now read it and decide.request_id, request_ref, type, status, total_amount, currency, routed_at
request.unroutedA request routed to you was routed elsewhere or unrouted. You can no longer act on it.request_id, request_ref, unrouted_at
offer.acceptedYour offer was accepted (request_status is approved once the seller accepted). You can fund it.offer_id, request_id, request_ref, request_status, rate, advance_pct, days, accepted_at
notice.acknowledgedA debtor acknowledged the assignment notice. One event per debtor; channel is online or offline.notice_id, request_id, request_ref, debtor_name, debtor_tax_id, invoice_count, total_amount, channel, decided_at
notice.disputedA debtor rejected the assignment notice. One event per debtor.The fields of notice.acknowledged, plus reason: the debtor's words, at most 500 characters.
request.fundedA request routed to you was funded.The request fields of request.routed, plus funded_at and advance_amount.
settlement.recordedMoney received on a funded request was recorded, partial or final: the payment that settles the request has final true.settlement_id, request_id, request_ref, amount, received_at, reference, received_total, request_total, outstanding, final, request_status
seller.documents_expiringA dated document (for example a tax or social-security certificate) of a seller with an open or funded request routed to you expires in 30 days, in 7 days or today. Checked daily; threshold_days is 30, 7 or 0.seller_id, company_name, company_tax_id, document_id, document_type, valid_until, days_left, threshold_days, request_ids
dispute.openedA debtor disputed invoices on a request routed to you, after approval or funding. reason is goods_not_received, defective, amount_incorrect, already_paid, not_our_invoice or other; channel is cession_page, letter or email.dispute_id, request_id, request_ref, notice_id, debtor_name, debtor_tax_id, invoice_numbers, reason, description, amount_disputed, channel, reported_by, received_on, request_status, status
dispute.resolvedAdvanta operations closed a dispute; status is resolved_for_debtor, resolved_for_seller or withdrawn.The fields of dispute.opened, with its status, plus resolved_at and resolution_note.
onboarding.completedA supplier you invited with an onboarding link submitted its company verification. status is completed; metadata is yours, as you sent it. For a link made with a sandbox key, only that key's endpoints receive it.onboarding_link_id, status, company_name, company_tax_id, country, seller_id, account_created_at, completed_at, verification_status, metadata
client.joinedA company you invited (by name, through your open link, a prescriber's link or the API) accepted the invitation and is now yours alone: its requests reach only you. origin says how it came (named, open_link, prescriber or api), with the prescriber when there is one.seller_id, invitation_id, company_name, company_tax_id, country, origin, channel, link_id, prescriber_id, prescriber_name, arrived_at, joined_at, mode
export.readyAn export you asked for (portal or API) is ready: download it with GET /v1/exports/{id} within 24 hours. It carries its manifest_sha256, never a link.export_id, status, requested_via, manifest_sha256, size_bytes, file_count, documents_mode, volumes, completed_at, expires_at
request.announcedA confidential request routed to you was announced: by you, by Advanta operations or by a trigger you turned on (trigger is manual, overdue, dispute or kyc_lapsed). Its prepared notices are being sent; notices lists each with its document's SHA-256.request_id, request_ref, trigger, reason, status, notices, announced_at
recourse.openedA funded request of yours is not settled recourse_days after an invoice's due date: a recourse claim against the seller (kind recourse) or a debtor default (kind debtor_default).claim_id, request_id, request_ref, kind, claim_amount, funded_amount, fee_amount, collected_before, first_due_date, days_after_due, invoice_ids, pay_reference
recourse.settledA claim was settled: by the payments you recorded since it opened (via statement or manual; the request closes as settled by recourse), or because the request settled the ordinary way (via request_settled, without paid).claim_id, request_id, request_ref, kind, claim_amount, paid, via, request_closed
payment.reached_sellerOn an account the seller shares with you, a payment came in from a debtor of a request you funded under confidential terms: its reference carries a financed invoice's number (match_rule invoice_reference), or the payer and the amount match (payer_and_amount).alert_id, request_id, invoice_ids, payer_name, payer_iban, amount, currency, booking_date, match_rule, confidence
opportunities.dailyThe morning's digest has something in it: invoices of your own clients that became eligible since the previous digest, or a change in those you were shown. A summary only, counts and totals, never an invoice, a debtor or a client: read the lines with GET /v1/opportunities?date= (lines). At most one a day; none on a day with nothing new and nothing changed.digest_id, date, stale, snapshot_at, clients (sharing, not_sharing), eligible (count, total), new (count, total), changes (paid, disputed, no_longer_eligible), currency, lines
debtor.watch_blockedAn official bulletin (the BORME for Spanish companies, Citius for Portuguese ones) published an act that blocks a debtor of a request you funded for new financing: insolvency declared or in progress, liquidation, insolvency concluded for lack of assets, dissolution, extinction, or the register sheet closed by the tax agency or after an insolvency. While it stands, no new offer and no new funding go to this debtor's invoices. One event per request and per change into blocked. It is what the bulletin published, for you to review, not a legal conclusion; only companies in Spain and Portugal are watched.request_id, request_ref, debtor (country, tax_id, name), status, note, events (kind, published_on, source, url), detected_at
pre_offer.acceptedA client requested the invoices of a pre-offer you sent it from your morning opportunities: its request, made with every usual check, now carries the pre-offer and is routed to you as always (request.routed), and your credit box prepares your offer from your grid. terms are what your grid gave when you sent it, indicative: the price is yours, Advanta states none. Once per pre-offer.pre_offer_id, request_id, request_ref, seller_id, company_name, company_tax_id, invoice_ids, invoice_count, total_amount, currency, terms, sent_at, valid_until, accepted_at
webhook.testThe test event you asked for with POST /v1/webhooks/{id}/test.

The delivery

Every delivery is a POST with a JSON body, sent byte for byte as stored (its SHA-256 is in the delivery log), with three headers of its own:

  • Advanta-Signature: t=<unix seconds>,v1=<hex>, see Verifying signatures.
  • Advanta-Event-Id: the event id, also id in the body.
  • Advanta-Event-Type: the event type, also type in the body.
Headers
POST /advanta HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
Advanta-Signature: t=1790179835,v1=5f2b…
Advanta-Event-Id: 9b2f7f64-5a1c-4a57-9d8a-0f2e3c4b5a69
Advanta-Event-Type: request.funded
Body
{
  "id": "9b2f7f64-5a1c-4a57-9d8a-0f2e3c4b5a69",
  "object": "event",
  "type": "request.funded",
  "api_version": "2026-09-23",
  "created": 1790179835,
  "environment": "sandbox",
  "data": {
    "request_id": "4c1e0d2a-8f3b-4f6e-9a55-3c2b1d0e9f87",
    "request_ref": "FR-2026-0042",
    "type": "factoring",
    "status": "funded",
    "total_amount": 18450.00,
    "currency": "EUR",
    "funded_at": "2026-09-23T18:10:35Z",
    "advance_amount": 16605.00
  }
}
FieldMeaning
idThe event id, the same as Advanta-Event-Id.
objectAlways event.
typeOne of the event types above.
api_versionThe API revision the event is written in.
createdWhen the event was created, in Unix seconds.
environmentlive, sandbox or unclassified, as in GET /v1/me.
dataThe event's fields, listed above.

Delivery and retries

  • Answer with any 2xx within 10 seconds and the event is delivered. Any other answer, no answer within 10 seconds, or a redirect is a failure; redirects are never followed.
  • A failed delivery is tried again after 1, 2, 4, 8 … minutes, doubling, at most 6 hours apart, with a last try 24 hours after the event. After that it is marked failed and stays in the delivery log.
  • Delivery is at least once. Keep the Advanta-Event-Id values you processed and ignore a repeat.
  • GET /v1/webhooks/{id}/deliveries shows each delivery's status, attempts, response code, last error and next attempt. POST /v1/webhooks/{id}/test sends a webhook.test event when you want to check your endpoint.

Endpoints and secrets

  • An endpoint URL is https on a public host name, on port 443, with no user name or password in it. Private, loopback and link-local addresses are refused.
  • At most 10 active endpoints. Each subscribes to a list of event types, or to ["*"] for all of them (the default).
  • The signing secret (whsec_ followed by 64 hexadecimal characters) is returned once: when the endpoint is created, and again each time you rotate it. Keep it as you would a password.
  • To rotate, call POST /v1/webhooks/{id}/rotate-secret. The previous secret keeps signing, as a second v1 in the header, for keep_previous_hours (0 to 72, default 24). Deploy the new secret within that window and no event is lost.

Verifying signatures

Every delivery carries Advanta-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>. Check it before you trust the body:

  1. Read the raw request body, exactly as received, before any JSON parsing.
  2. Compute HMAC-SHA256 over <t>.<raw body>, keyed with your endpoint secret (the whole whsec_… string, as UTF-8), and hex-encode it.
  3. Accept the delivery if any v1 in the header matches, compared in constant time (after a rotation there are two), and t is within 300 seconds of your clock. Reject it otherwise.

A rejected delivery does not answer 2xx, so Advanta retries it like any other failure.

Node.js
import crypto from 'node:crypto';
import express from 'express';

const TOLERANCE_SECONDS = 300;

// header:  the Advanta-Signature value
// rawBody: the request body as a Buffer, exactly as received (before any JSON parsing)
// secret:  your endpoint secret, the whole "whsec_…" string
export function verifyAdvantaSignature(header, rawBody, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
  let t = null;
  const signatures = [];
  for (const part of String(header ?? '').split(',')) {
    const i = part.indexOf('=');
    if (i <= 0) continue;
    const key = part.slice(0, i).trim();
    const value = part.slice(i + 1).trim();
    if (key === 't' && /^[0-9]{1,12}$/.test(value)) t = Number(value);
    else if (key === 'v1' && /^[0-9a-f]{64}$/.test(value)) signatures.push(value);
  }
  if (t === null || signatures.length === 0) return false;
  if (Math.abs(nowSeconds - t) > TOLERANCE_SECONDS) return false; // replay window

  const expected = crypto.createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest();
  // After a rotation the header carries two v1 values: accept either.
  return signatures.some((sig) => crypto.timingSafeEqual(Buffer.from(sig, 'hex'), expected));
}

// Express: keep the body raw on this route.
const app = express();
app.post('/advanta', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyAdvantaSignature(req.get('Advanta-Signature'), req.body, process.env.ADVANTA_WEBHOOK_SECRET)) {
    return res.status(400).end();
  }
  const event = JSON.parse(req.body.toString('utf8'));
  // Delivery is at least once: skip an event.id you already processed, then queue the work.
  res.status(200).end();
});
Python
import hashlib
import hmac
import json
import os
import re
import time

from flask import Flask, request

TOLERANCE_SECONDS = 300


def verify_advanta_signature(header, raw_body, secret, now=None):
    """header: the Advanta-Signature value; raw_body: the request body as bytes, exactly
    as received; secret: your endpoint secret, the whole "whsec_..." string."""
    t = None
    signatures = []
    for part in (header or "").split(","):
        key, sep, value = part.partition("=")
        key, value = key.strip(), value.strip()
        if not sep:
            continue
        if key == "t" and re.fullmatch(r"[0-9]{1,12}", value):
            t = int(value)
        elif key == "v1" and re.fullmatch(r"[0-9a-f]{64}", value):
            signatures.append(value)
    if t is None or not signatures:
        return False
    now = int(time.time()) if now is None else now
    if abs(now - t) > TOLERANCE_SECONDS:  # replay window
        return False
    expected = hmac.new(secret.encode("utf-8"), f"{t}.".encode("ascii") + raw_body, hashlib.sha256).hexdigest()
    # After a rotation the header carries two v1 values: accept either.
    return any(hmac.compare_digest(sig, expected) for sig in signatures)


# Flask
app = Flask(__name__)


@app.post("/advanta")
def advanta_webhook():
    raw = request.get_data()  # the raw bytes; do not read request.json before verifying
    if not verify_advanta_signature(request.headers.get("Advanta-Signature"), raw, os.environ["ADVANTA_WEBHOOK_SECRET"]):
        return "", 400
    event = json.loads(raw)
    # Delivery is at least once: skip an event["id"] you already processed, then queue the work.
    return "", 200

Errors

Every error is JSON, with a stable code and a message for people. Branch on the code, never on the message.

JSON
{
  "error": {
    "code": "not_routed_to_you",
    "message": "This request is not assigned to your institution. Advanta operations routes requests."
  }
}

Status codes

StatusCodesMeaning
400reason_requiredA decline without reason or notes.
401unauthorized, key_expiredunauthorized: the key is missing, malformed, unknown, revoked or inactive. key_expired: the key expired.
403forbidden, not_your_request, export_live_key_required, not_routed_to_you, not_your_claimforbidden: the key lacks the scope, or the funder is not active. not_your_request: a settlement from a funder that is not the funder of record. export_live_key_required: a sandbox key cannot export. not_routed_to_you: announcing a request that is not routed to you. not_your_claim: closing a recourse claim that is not on a request of yours.
404not_foundNo such resource visible to this key, or an unknown route.
405method_not_allowedThe method is not supported on this path.
409not_routed_to_you, invalid_state, credit_limit, funding_gate_failed, funding_amount_mismatch, debtor_not_eligible_for_funder, documents_missing_for_funder, level_lower, notice_already_decided, not_factoring, request_not_in_flight, statement_already_imported, too_many_endpoints, prescriber_exists, link_exists, company_already_registered, email_has_account, invitation_pending_exists, country_not_served, export_in_progress, export_not_ready, export_failedA write the request does not allow: not routed to you, the wrong status, beyond one of your limits, a factoring notice not yet acknowledged, a debtor outside your list, a required seller document missing or out of date, or a consent level that would go down, comes after a debtor answered, or targets a request that is not factoring or not in flight. Also a statement file already imported, more than 10 active endpoints, a prescriber name you already use, a link that still works (send replace: true), and an onboarding link for a company or contact that already has an account or a pending invitation, or in a country Advanta does not take sellers from yet. An export is still being built (export_in_progress, export_not_ready) or failed (export_failed).
410export_expiredexport_expired: the export's files could be downloaded for 24 hours; start a new export.
413file_too_large, too_many_lines, too_many_rowsThe statement is larger than 4 MiB or has more than 5,000 transactions, or a limits import has more than 2,000 rows (too_many_rows).
422invalid_terms, invalid_amount, invalid_received_at, invalid_funding_details, step_back_message_required, invalid_decision, level_invalid, invalid_flag, reason_required, debtor_tax_id_required, seller_id_required, invalid_limit, tax_id_required, file_required, file_unreadable, file_empty, statement_unreadable, statement_has_no_transactions, line_invalid, country_invalid, codes_invalid, note_invalid, country_without_catalogue, document_code_unknown, body_required, url_required, url_invalid, url_too_long, url_must_be_https, url_has_credentials, url_port_not_allowed, url_host_not_allowed, url_not_allowed, events_invalid, unknown_event_type, invalid_description, invalid_keep_previous_hours, body_invalid, name_required, name_invalid, email_invalid, email_not_reserved, notes_too_long, lang_invalid, prescriber_id_invalid, expires_at_invalid, replace_invalid, reason_invalid, company_vat_invalid, contact_email_invalid, contact_name_invalid, company_name_invalid, company_name_required, return_url_invalid, return_url_not_allowed, metadata_invalid, locale_invalid, valid_days_invalid, prescriber_not_found, country_not_served, seller_id_invalid, terms_invalid, terms_key_unknown, seller_not_in_programme, already_announced, request_closed, status_invalid, close_invalid, claim_closed, rows_required, invalid_source, source_ref_invalid, source_ref_too_long, invalid_dateA field or parameter is missing or invalid; the code says which. (A decline without a reason is 400 reason_required; a settings or consent-level change without one is 422 reason_required.) Also a change of terms for a company outside your programme (seller_not_in_programme), a request already announced or closed (already_announced, request_closed), and a recourse claim no longer open or closed the wrong way (claim_closed, close_invalid). A limits import without rows (rows_required), with a source other than import or insurer (invalid_source) or with a source_ref that is not text or is longer than 200 characters (source_ref_invalid, source_ref_too_long) is refused too, as is an opportunities date that is not a calendar day (invalid_date).
429rate_limited, export_rate_limitedrate_limited: more calls than the key's limit (120 a minute and 3,000 an hour, unless Advanta set another for the key), or repeated calls with an unknown key from one address; send it again after Retry-After seconds. export_rate_limited: at most 10 exports in 24 hours.
500internal, query_failed, write_failed, ingest_failedSomething failed on Advanta's side. Retry later.

debtor_not_eligible_for_funder

When you take listed buyers only, an offer on a request with a debtor outside your list answers 409 with the debtors that block it, each with a reason:

JSON
{
  "error": {
    "code": "debtor_not_eligible_for_funder",
    "message": "…",
    "debtors": [
      {
        "debtor_key": "A00000000",
        "name": "Example Buyer, S.A.",
        "country": "ES",
        "reason": "not_listed",
        "invoices": 2
      }
    ]
  }
}
reasonMeaning
not_listedThe debtor is not on your list.
refusedYour limit on the debtor is set to refused.
suspendedYour limit on the debtor is suspended.
no_tax_idAn invoice has no buyer tax number.
invoice_missingA line of the request has no invoice.
no_invoicesThe request has no invoices.

documents_missing_for_funder

When a document you require from the seller is missing, unverified or out of date, an offer or a funding answers 409 with the documents that block it, each with a reason:

JSON
{
  "error": {
    "code": "documents_missing_for_funder",
    "message": "…",
    "documents": [
      {
        "code": "cert_seguridad_social",
        "name": "…",
        "reason": "expired",
        "valid_until": "2026-09-15",
        "document_id": "5b7c9d1e-3f2a-4b6c-8d0e-1a2b3c4d5e6f"
      }
    ]
  }
}
reasonMeaning
missingThe seller has not provided the document.
unverifiedAdvanta has not verified it yet.
expiredIt is not valid on that date any more.
rejectedAdvanta rejected it.
no_validity_dateIt has no validity date.
not_in_catalogueThe code is not in the catalogue for the seller's country.

Versioning and limits

The path carries the major version, /v1. Within v1, fields and event types can be added: ignore what you do not know.

api_version, in GET /v1/me and in every webhook event, names the revision you are reading: 2026-09-23 today.

Each key may make 120 calls a minute and 3,000 an hour, counted across every Advanta server (Advanta operations can set another limit for a key). A call beyond the limit is answered 429 rate_limited, with a Retry-After header in seconds and retry_after_seconds, limit and window_seconds in the error: nothing it asked for was done, so send it again after that time. Repeated calls with an unknown or malformed key from one address are also answered 429. GET /v1/health is not limited, and exports keep their own limit (export_rate_limited).

Everything on this page is described in openapi.yaml (OpenAPI 3.1). Questions, or a key: diogo@advanta.pt.