> ## Documentation Index
> Fetch the complete documentation index at: https://developers.novatrade24.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Buyer onboarding

> Create or sync a buyer by VAT number, upload KYC documents, run VIES, and optionally trigger NT24-led verification.

Buyers are the starting point of every Novatrade24 transaction. A buyer in
the API is a `TradePartner` entity acting in the buyer role — unique per
`(sellerId, vatNumber)`, so **the same buyer across multiple orders is one
record**, not a new entry per deal.

## Upsert a buyer

`POST /partners/{sellerId}/buyers` is an upsert keyed on
`(sellerId, vatNumber)`:

* **First call** → buyer created, `201 Created` + `Location` header.
* **Second call with identical body** → existing buyer returned, `200 OK`.
* **Second call with different body** → `409 Conflict` with current resource
  (use `PATCH` to explicitly modify fields).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "kycMode": "MARKETPLACE_LED",
      "vatNumber": "DE123456789",
      "companyName": "AutoHandel Mustermann GmbH",
      "address": {
        "street": "Hauptstraße 42",
        "postCode": "10115",
        "city": "Berlin",
        "country": "DE"
      },
      "contacts": [
        {
          "firstName": "Erika",
          "lastName": "Mustermann",
          "email": "erika@automustermann.de"
        }
      ]
    }'
  ```

  ```typescript TypeScript theme={null}
  const buyer = await fetch(
    `https://api.novatrade24.com/v1/partners/${sellerId}/buyers`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Idempotency-Key': crypto.randomUUID(),
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        kycMode: 'MARKETPLACE_LED',
        vatNumber: 'DE123456789',
        companyName: 'AutoHandel Mustermann GmbH',
        address: { street: 'Hauptstraße 42', postCode: '10115', city: 'Berlin', country: 'DE' },
        contacts: [{ firstName: 'Erika', lastName: 'Mustermann', email: 'erika@automustermann.de' }],
      }),
    }
  ).then(r => r.json());
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "uuid": "a1b2c3d4-...",
  "vatNumber": "DE123456789",
  "companyName": "AutoHandel Mustermann GmbH",
  "address": { ... },
  "kycMode": "MARKETPLACE_LED",
  "status": "ACTIVE",
  "version": 0,
  "createdAt": "2026-04-22T10:00:00Z",
  "updatedAt": "2026-04-22T10:00:00Z"
}
```

<Note>
  Save the returned `uuid` — it's the buyer reference for every subsequent
  call (order creation, KYC documents, invitations). The `version` field is
  for [optimistic concurrency](/concepts/optimistic-concurrency).
</Note>

## Choose a KYC mode

See the full [KYC modes](/guides/kyc-modes) guide. Short version:

| Mode                     | Who runs KYC          | Output                                     |
| ------------------------ | --------------------- | ------------------------------------------ |
| **MARKETPLACE\_LED** (A) | You / the marketplace | Raw documents + VIES check only            |
| **NT24\_LED** (B)        | Novatrade24           | Full iDenfy + AML + signed KYC profile PDF |

Mode is required on buyer create, can be upgraded A→B (not reversed) via
`PATCH /buyers/{id}`. `MARKETPLACE_LED` is rejected with `403` if your
organization's `allowMarketplaceLedKyc` capability is `false`.

## Upload KYC documents

Documents are typed via the platform `DocumentType` enum. For a KYC buyer
workflow, the relevant types are `COC_*`, `BANK_*`, `VAT_*`, `CONTACT_*`,
`ADDRESS_*`, `TP_*`, and `AML_DECLARATION`.

```bash theme={null}
curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/kyc/documents" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "type=COC_EXTRACT" \
  -F "documentDate=2026-01-15" \
  -F "file=@/path/to/coc-extract.pdf"
```

Documents are **append-only** — each upload is a new version. To supersede
a bad upload, archive it:

```bash theme={null}
curl -X PATCH "https://api.novatrade24.com/v1/documents/${DOC_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "status": "ARCHIVED" }'
```

The latest `ACTIVE` document of each type is what counts for compliance.
Envers keeps the full history for audit.

## Trigger a VIES check

```bash theme={null}
curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/kyc/vies-check" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)"
```

Returns `202 Accepted`. Result is written to KYC status shortly after (async).
Check `GET .../kyc`:

```json theme={null}
{
  "buyerId": "...",
  "kycMode": "MARKETPLACE_LED",
  "overallStatus": "VALID",
  "viesLastCheckedAt": "2026-04-22T10:01:30Z",
  "viesResult": "VALID",
  "documentsComplete": true
}
```

Failing VIES checks (before or after pickup) also emit the
`kyc.vies_check_failed` webhook — subscribe for real-time notification.

## Optional: invite the buyer to complete their own KYC

If you don't have all KYC data yourself, generate a self-service invitation
— the buyer receives an email with a branded upload link.

```bash theme={null}
curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/kyc/self-service-invitations" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "recipientEmail": "erika@automustermann.de",
    "expiresInDays": 14,
    "language": "de"
  }'
```

Response:

```json theme={null}
{
  "uuid": "inv_...",
  "kind": "KYC",
  "recipientEmail": "erika@automustermann.de",
  "status": "ACTIVE",
  "publicLinkUrl": "https://onboard.novatrade24.com/kyc/a1b2c3d4...",
  "createdAt": "2026-04-22T10:10:00Z",
  "expiresAt": "2026-05-06T10:10:00Z",
  "usageCount": 0
}
```

When the buyer uses the link, the `self_service_invitation.used` webhook
fires. The KYC status endpoint reflects their submissions identically to
API-uploaded documents — your integration doesn't need to branch on data source.

## NT24-led verification (Mode B only)

If the buyer's `kycMode` is `NT24_LED`, trigger iDenfy + AML:

```bash theme={null}
curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/kyc/verify" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)"
```

Returns the iDenfy session URL. Send that URL to the buyer; they complete
the verification in-browser. NT24 fires `kyc.verification_completed` with
the outcome (`VERIFIED` or `REJECTED`).

Once verified, download the signed KYC profile PDF:

```bash theme={null}
curl "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/kyc/profile" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -o kyc-profile.pdf
```

## Block or unblock a buyer

Used for risk-based interventions (fraud signal, regulatory hold):

```bash theme={null}
curl -X POST "https://api.novatrade24.com/v1/partners/${SELLER_ID}/buyers/${BUYER_ID}/block" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Manual review — possible VIN-cloning pattern" }'
```

Blocked buyers cannot have new orders created. Existing orders continue per
their current state.

## Next

<CardGroup cols={2}>
  <Card title="KYC modes deep-dive" icon="list-check" href="/guides/kyc-modes">
    Differences between Marketplace-led and NT24-led.
  </Card>

  <Card title="Create an order" icon="file-invoice" href="/guides/order-creation">
    With a buyer in place, register your first transaction.
  </Card>
</CardGroup>
