Import vendors and contracts
Moving a register in from a spreadsheet, another GRC tool or an ERP export. Vendors have a bulk import endpoint; contracts do not — and the second half of that sentence is the part worth reading before you plan a migration.
| What you are loading | Route |
|---|---|
| Vendors | POST /v1/vendors/imports — this guide |
| Contracts | the CSV / XLSX importer in the app, not the API |
| Documents onto an existing contract | POST /v1/contracts/{id}/documents |
Vendors
Section titled “Vendors”One request carries the rows and the options. Cells are strings, keyed by importable field name:
curl -X POST https://api.vendorica.com/v1/vendors/imports \ -H "Authorization: Bearer $VENDORICA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "label": "Q3 ERP export", "matchKey": { "key": "lei", "mode": "upsert" }, "enrich": true, "vendors": [ { "name": "Acme BV", "lei": "724500VKKSH9QOLTFR81", "website": "acme.example" }, { "name": "Globex GmbH", "vatNumber": "DE123456789" } ] }'Scope: vendors:write. Up to 10,000 rows per request.
It runs the same validate → dedupe → commit pipeline as the in-app wizard and
mints an IMP-{n} history entry, so an API import and a spreadsheet import
land in the same audit record and are reviewed the same way.
The response tells you what happened, per row
Section titled “The response tells you what happened, per row”The call is synchronous — the outcome is in the response body, not behind a job you have to poll:
{ "success": true, "data": { "job": { "id": "f00e1550-382b-4016-8193-d10f06b21451", "displayId": "IMP-42", "status": "completed", "rowsTotal": 2, "rowsImported": 1, "rowsUpdated": 1, "rowsSkipped": 0, "rowsFailed": 0 }, "summary": { "imported": 1, "updated": 1, "skipped": 0, "failed": 0 } }}failed is not an error status — a request can return 200 with rows that
did not land. Check summary.failed, not just the HTTP code.
Choosing a match key
Section titled “Choosing a match key”matchKey.key is one of name, website, vatNumber or lei; mode is
create-only (default) or upsert.
Prefer lei where your data has it: it is the only one of the four that
is a registered identifier rather than a string someone typed. name is the
default because every file has one, and it is also the weakest — “Acme BV”,
“Acme B.V.” and “ACME BV” are three vendors to a matcher and one to you.
LEI enrichment
Section titled “LEI enrichment”enrich: true fills an empty countryOfRegistration from GLEIF for rows
whose LEI passes its checksum. Bounded and best-effort: it never overwrites a
value you supplied, and a GLEIF timeout costs you the enrichment, not the
import.
Contracts
Section titled “Contracts”The importer in the app (Contracts → Import) takes CSV or XLSX, supports the same upsert semantics, and matches each row to a vendor.
Two things that catch people out, both deliberate:
vendorNamemust be mapped, even in a file where every row carries an LEI. The LEI still resolves the vendor at validation — the mapping requirement is there to keep the name column honest against it.- There is no “create missing vendors” toggle. A row whose vendor does not resolve is a row error, pointing you at the vendor importer. This is why the order in a migration is vendors first, contracts second.
If you match on title, be aware it can false-positive across vendors —
two suppliers both filing “Master Services Agreement”. Use
arrangementReference where you have one.
Plan gating
Section titled “Plan gating”POST /v1/vendors/imports requires the vendors.bulk_import and
vendors.import_integrations features. Without them the call returns
402, not 403 — the request was understood and authorised, and the
plan is what refused it.
Pacing
Section titled “Pacing”An import request is a single write, so the write budget is not usually what binds. What does, on a migration that also carries files, is the upload budget: 60/min, and it stacks with writes.
For anything at migration scale, ask support to raise the budget on your API
client before you start. That is what the per-client override exists for, and
it is a faster conversation before the first 429 than after it. See
rate limits.