Importing data via the REST API
Overview
When migrating to Kyvento from another system, you transfer your existing records – customers, subscriptions, settled and open invoices, and the associated payments – via the Kyvento REST API. This article describes the order of transfer, the endpoints provided for it, and the settings that allow an interrupted import run to be repeated safely.
No file-based bulk import: Kyvento does not offer a file upload for bulk data. Such an import would bypass the domain services that apply whenever a record is created – validation of the VAT identification number, tax rate resolution, and the events on which dunning, accounting exports and reporting are based. The transfer therefore uses the same endpoints as your ongoing integration, one record at a time.
Requirements
- A personal access token (see Setting up API access and creating an API key). Pass it with every call in the
Authorization: Bearer <token>header. - The required abilities on the token:
customers:full,subscriptions:fullandinvoices:full. A token with read-only access is not sufficient for the transfer. - All products and coupons referenced by your subscriptions must already exist in Kyvento. Create them beforehand – either in the interface or via
POST /api/v1/productsandPOST /api/v1/coupons. - An export from your previous system in which every record has a unique and permanent identifier. This identifier is required to make the import repeatable.
Order of transfer
Transfer the data in the following order, as each step builds on the IDs from the preceding one. The base address for all endpoints is https://api.kyvento.com/v1.
| Step | Record type | Endpoint |
|---|---|---|
| 1 | Customers | POST /api/v1/customers |
| 2 | Subscriptions for those customers | POST /api/v1/subscriptions |
| 3 | Settled and open invoices | POST /api/v1/invoices followed by POST /api/v1/invoices/{invoice}/finalize |
| 4 | Payments for those invoices | POST /api/v1/invoices/{invoice}/record-payment |
Step 1: Create customers
Begin with the master data. Two particularities apply here:
customer_typeis a required field and must contain eitherbusinessorindividual. Among other things, the tax treatment depends on this value.- Address fields are passed flat. A nested address object is not supported; street, house number, postal code, city and country are located directly at the top level.
POST /api/v1/customers
Authorization: Bearer 42|aBcDe…
Content-Type: application/json
{
"customer_type": "business",
"company_name": "Nordwind Software GmbH",
"email": "buchhaltung@nordwind-software.example",
"vat_id": "DE347829165",
"street": "Musterstraße",
"house_number": "12",
"postal_code": "20095",
"city": "Hamburg",
"country": "DE"
}
Please note regarding field names: fields that the endpoint does not recognise are discarded without an error message. A nested address object will therefore result in a customer without an address. We recommend reviewing the first transferred record in the interface before transferring the entire dataset.
Step 2: Create subscriptions
Next, create the active contracts via POST /api/v1/subscriptions – using the customer ID from step 1 and the product set up beforehand. Carry over the original contract start date and the next billing date from your previous system so that the first invoice issued by Kyvento follows on directly from the final invoice of the previous system.
Step 3: Create and finalize invoices
Invoices are transferred via POST /api/v1/invoices. Two particularities are decisive here:
Invoices require line items
The total fields you pass apply to the draft only. On finalization, Kyvento recalculates the invoice amount from the line items (items). An invoice without line items therefore shows an amount of 0.00 and will not accept any payment afterwards. Pass at least one line item:
POST /api/v1/invoices
Authorization: Bearer 42|aBcDe…
Content-Type: application/json
{
"customer_id": 42,
"items": [
{
"description": "Maintenance 07/2026 (previous system)",
"quantity": 1,
"unit_price": 100.00,
"tax_rate": 19
}
]
}
Finalization before recording a payment
POST /api/v1/invoices initially creates a draft. It becomes a document only through POST /api/v1/invoices/{invoice}/finalize. Recording a payment against a draft is rejected, so finalize the invoice before proceeding to step 4.
Step 4: Record payments
Invoices already settled in your previous system receive their payment via POST /api/v1/invoices/{invoice}/record-payment. This endpoint documents a payment that has already taken place and does not initiate a payment transaction.
Difference between record-payment and collect: while record-payment documents a payment that has already taken place, POST /api/v1/invoices/{invoice}/collect initiates an actual payment collection. When transferring invoices that have already been settled, this would result in your customers being charged a second time. For data transfers, record-payment is the intended endpoint.
Repeatability via the idempotency key
An import run may be interrupted for technical reasons, for example by a dropped connection. All four endpoints therefore support the Idempotency-Key header (a UUID v4). Kyvento stores the key that was passed: if the same key arrives again, you receive the original response without a second record being created.
For this safeguard to take effect on a repeated run, the key must be derived from the source record and be identical on every pass – for example as a UUID v5 over a string of the form previous-system:customer:4711. A random key generated per attempt cancels out the effect and results in duplicate records on the second run.
POST /api/v1/customers
Authorization: Bearer 42|aBcDe…
Idempotency-Key: 8c4f1e2a-5d7b-5f3c-9a1e-6b2d4c8e0f37
Content-Type: application/json
VAT identification numbers
A vat_id that is passed in is validated against the EU VIES portal after creation, not during it. The customer is created in any case and receives a validation status that you can query later. We recommend not pausing the import until the validation result is available, as the availability of VIES is outside our sphere of influence. Transfer the complete dataset first and evaluate the validation statuses afterwards.
Rate limits
The same per-minute limits apply to a data transfer as to any other API use on your plan. Evaluate the X-RateLimit-Remaining header and, when a response returns status 429, observe the waiting period specified in the Retry-After header; a fixed waiting period leads either to further failed attempts or to unnecessary delay. Further guidance can be found in API rate limits and best practices.
Verifying success
- Trial run with a single record: begin by transferring one customer with one subscription, one invoice and one payment.
- Review in the interface: are the customer's address and VAT identification number complete? Does the invoice show the expected amount? Is it marked as paid?
- Verify repeatability: run the same record through your script a second time. If this creates a duplicate, the idempotency keys are not derived stably and should be adjusted before the full transfer.
- Complete transfer and reconciliation: finally, compare the number of customers, subscriptions and open items against your previous system.
If it does not work
- Invoice shows 0.00: no line items (
items) were passed. Create the invoice again with line items; a subsequent correction of the amount is not supported. - Payment is rejected: the invoice is still in draft status. Call
finalizefirst. - Customer without an address: the address fields were passed nested and were therefore discarded. Use the flat field names from step 1.
- Status 401 or 403: the token is missing, has expired, or does not hold the required ability (
customers:full,subscriptions:full,invoices:full). - Status 429: the rate limit has been reached. Continue the transfer after the waiting period specified in
Retry-After. - Duplicate records after a repeated run: the idempotency keys are generated anew per attempt. Derive them from the identifier of the source record instead.
Extensive data transfers
You do not have to carry out extensive or complex data transfers yourself. We offer data imports and migrations as a paid additional service; orders are placed in advance through an individual agreement covering scope and content.
Please contact support@kyvento.com and let us know which system you are migrating from and what volume of data is to be transferred. You will then receive a quote covering the applicable costs.
Next steps
- Set up access and abilities – see Setting up API access and creating an API key
- Endpoints, fields and example calls in detail – see API documentation and examples
- Take rate limits into account – see API rate limits and best practices
- Stay informed through events after the transfer – see Configuring webhooks