# Verification

`GET /partner/v1/sandbox/verification` is the definition of done for the integration. It reports 15 checks — 12 required, 3 optional. Each passes when your integration performs a specific API interaction, observed by the server; nothing is self-reported. `readyForProduction` is `true` when all 12 required checks pass. Checks are **sticky**: once passed they stay passed until `POST /partner/v1/sandbox/reset`, which sets every check back to `pending`. A check counts for the whole sandbox, whichever of your two companies or three keys made the call.

Do not report the integration complete until this endpoint returns `"readyForProduction": true`. Run the flows through your integration code, not hand-typed curls — the checks cannot tell the difference, but only the former proves your integration.

## The endpoint

```bash
curl -s https://sandbox.bilnex.io/partner/v1/sandbox/verification \
  -H "Authorization: Bearer <YOUR_API_KEY>"
```

Response (early in an integration — two checks passed):

```json
{
  "checks": [
    { "id": "AUTH_OK", "required": true, "status": "pass", "detail": "GET /partner/v1/me succeeded with this key.", "fixHint": null, "docsUrl": "https://sandbox.bilnex.io/docs/authentication.md" },
    { "id": "CAPABILITY_CHECKED_CAPABLE", "required": true, "status": "pass", "detail": "Capability checked for 95000041 (capable).", "fixHint": null, "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "CAPABILITY_CHECKED_NOT_CAPABLE", "required": true, "status": "pending", "detail": null, "fixHint": "Call GET /partner/v1/customers/95999902/e-invoice-capability — your code must handle the eInvoiceCapable=false branch, not just the happy path.", "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "INVOICE_CREATED", "required": true, "status": "pending", "detail": null, "fixHint": "Create an invoice with POST /partner/v1/invoices.", "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "PDF_FETCHED", "required": true, "status": "pending", "detail": null, "fixHint": "Fetch the invoice PDF with GET /partner/v1/invoices/{id}/pdf.", "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "EINVOICE_SENT", "required": true, "status": "pending", "detail": null, "fixHint": "Send an invoice with POST /partner/v1/invoices/{id}/send and channel E_INVOICE.", "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "EINVOICE_RECEIVED_AT_RECEIVER", "required": true, "status": "pending", "detail": null, "fixHint": "Send an e-invoice to your own receiver company so the loopback can be proven. Use the receiver regCode from your provisioning response as the invoice customer.", "docsUrl": "https://sandbox.bilnex.io/docs/receiving-invoices.md" },
    { "id": "PURCHASE_LISTED", "required": true, "status": "pending", "detail": null, "fixHint": "List incoming documents with GET /partner/v1/purchase-invoices while acting as the receiver company (send header X-Company-Id: <receiver companyId>).", "docsUrl": "https://sandbox.bilnex.io/docs/receiving-invoices.md" },
    { "id": "MARKED_EXPORTED", "required": true, "status": "pending", "detail": null, "fixHint": "After importing a purchase invoice into the ERP, confirm it with POST /partner/v1/purchase-invoices/{id}/mark-exported.", "docsUrl": "https://sandbox.bilnex.io/docs/receiving-invoices.md" },
    { "id": "EMAIL_SENT", "required": true, "status": "pending", "detail": null, "fixHint": "Send an invoice with POST /partner/v1/invoices/{id}/send and channel EMAIL.", "docsUrl": "https://sandbox.bilnex.io/docs/email-invoices.md" },
    { "id": "EMAIL_FETCHED_FROM_INTERCEPT", "required": true, "status": "pending", "detail": null, "fixHint": "Sandbox email is never delivered. Read it back with GET /partner/v1/sandbox/emails and assert the invoice PDF is attached.", "docsUrl": "https://sandbox.bilnex.io/docs/email-invoices.md" },
    { "id": "NOT_CAPABLE_FALLBACK", "required": true, "status": "pending", "detail": null, "fixHint": "Prove the fallback path: after a capability check returns false for a recipient, send that recipient's invoice with channel EMAIL instead of E_INVOICE.", "docsUrl": "https://sandbox.bilnex.io/docs/sending-invoices.md" },
    { "id": "PEPPOL_SENT", "required": false, "status": "pending", "detail": null, "fixHint": "Optional, and not available yet: sending with channel PEPPOL answers 501 peppol_send_unavailable. Nothing to do.", "docsUrl": "https://sandbox.bilnex.io/docs/peppol.md" },
    { "id": "PEPPOL_RECEIVED", "required": false, "status": "pending", "detail": null, "fixHint": "Optional. Call POST /partner/v1/sandbox/trigger with event inbound_peppol. The check passes when the document arrives; Peppol documents are not yet listed by /partner/v1/purchase-invoices.", "docsUrl": "https://sandbox.bilnex.io/docs/peppol.md" },
    { "id": "INVOICE_SEEN_HANDLED", "required": false, "status": "pending", "detail": null, "fixHint": "Optional. Call POST /partner/v1/sandbox/trigger with event invoice_seen for an e-mailed invoice and handle the SEEN status in your ERP.", "docsUrl": "https://sandbox.bilnex.io/docs/email-invoices.md" }
  ],
  "readyForProduction": false,
  "nextSteps": [
    "CAPABILITY_CHECKED_NOT_CAPABLE: Call GET /partner/v1/customers/95999902/e-invoice-capability — your code must handle the eInvoiceCapable=false branch, not just the happy path.",
    "INVOICE_CREATED: Create an invoice with POST /partner/v1/invoices.",
    "PDF_FETCHED: Fetch the invoice PDF with GET /partner/v1/invoices/{id}/pdf."
  ]
}
```

Per check: `status` is `pass` or `pending` — there is no failed state; a check that has not passed yet is `pending`. `detail` records what was observed when the check passed (`null` while pending). `fixHint` says what to do (`null` once passed). `docsUrl` is where to read more. `nextSteps` lists up to three outstanding required checks, each as `"<id>: <fixHint>"`, in the order of the catalog; it is empty once `readyForProduction` is `true`.

## All 15 checks

### Required (all 12 must pass for `readyForProduction`)

| # | id | What passes it |
|---|---|---|
| 1 | `AUTH_OK` | A successful `GET /partner/v1/me` with your key. |
| 2 | `CAPABILITY_CHECKED_CAPABLE` | `GET /partner/v1/customers/{regCode}/e-invoice-capability` answering `eInvoiceCapable: true` — your receiver company, or `95999901`. |
| 3 | `CAPABILITY_CHECKED_NOT_CAPABLE` | The same endpoint answering `eInvoiceCapable: false` — `95999902`. |
| 4 | `INVOICE_CREATED` | A successful `POST /partner/v1/invoices` (201). |
| 5 | `PDF_FETCHED` | A successful `GET /partner/v1/invoices/{id}/pdf`. |
| 6 | `EINVOICE_SENT` | A `POST /partner/v1/invoices/{id}/send` with `channel: "E_INVOICE"` that answers 200. |
| 7 | `EINVOICE_RECEIVED_AT_RECEIVER` | An e-invoice your sender sent arrives at its recipient. Use your receiver company as the invoice's customer, so you can also list the document there. Delivery is asynchronous: the check passes a moment after the send. |
| 8 | `PURCHASE_LISTED` | A successful `GET /partner/v1/purchase-invoices`. Act as the receiver company, where the documents arrive. |
| 9 | `MARKED_EXPORTED` | A successful `POST /partner/v1/purchase-invoices/{id}/mark-exported`. |
| 10 | `EMAIL_SENT` | A send with `channel: "EMAIL"` that answers 200. |
| 11 | `EMAIL_FETCHED_FROM_INTERCEPT` | `GET /partner/v1/sandbox/emails` while it holds at least one captured email. |
| 12 | `NOT_CAPABLE_FALLBACK` | An EMAIL send (answering 200) of an invoice whose `customer.regCode` is `95999902`. Do it the way production needs it: check capability first, and choose EMAIL because the answer was `eInvoiceCapable: false`. |

### Optional (3 — not gating)

| # | id | What passes it |
|---|---|---|
| 13 | `PEPPOL_SENT` | Nothing yet: sending with `channel: "PEPPOL"` answers 501 `peppol_send_unavailable`, so this check cannot pass today. |
| 14 | `PEPPOL_RECEIVED` | `POST /partner/v1/sandbox/trigger` with `{"event":"inbound_peppol"}`. It passes when the Peppol document arrives at your receiver company. The document is not listed by `GET /partner/v1/purchase-invoices`: incoming Peppol documents are not yet returned by the API. |
| 15 | `INVOICE_SEEN_HANDLED` | `POST /partner/v1/sandbox/trigger` with `{"event":"invoice_seen","invoiceId":"..."}` for one of your invoices. It passes when the trigger fires; then re-read the invoice and handle status `SEEN` in your ERP. |

## The loop

```
loop:
  v = GET /partner/v1/sandbox/verification
  if v.readyForProduction: done — report with the JSON as evidence
  for check in v.checks where required and status == "pending":
    read check.fixHint and check.docsUrl
    fix the integration; run the flow through the integration
  # re-run at most every few seconds; checks update right after the qualifying call
  # (EINVOICE_RECEIVED_AT_RECEIVER a moment later, when the e-invoice is delivered)
```

After `readyForProduction: true`, continue with [go-live](https://sandbox.bilnex.io/docs/go-live.md): production is opened per integration on request — email **partners@bilnex.io** the verification response and your ERP's name. The optional checks do not gate anything.

API version 2026-08-01. /partner/v1 changes are additive-only; see [versioning-policy](https://sandbox.bilnex.io/docs/versioning-policy.md).

---
**Building with a coding agent?** Start from [agents.md](https://sandbox.bilnex.io/agents.md) or install the skill: `npx skills add https://sandbox.bilnex.io`. Machine-readable index: [llms.txt](https://sandbox.bilnex.io/llms.txt). Every page on this site is also plain markdown — append `.md`.
