API & webhooks

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:full and invoices: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/products and POST /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_type is a required field and must contain either business or individual. 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

  1. Trial run with a single record: begin by transferring one customer with one subscription, one invoice and one payment.
  2. 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?
  3. 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.
  4. 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 finalize first.
  • 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

← Back to Support

Related articles

API & webhooks

Verifying a webhook endpoint

Overview Kyvento delivers webhook events exclusively to verified endpoints. Verification establishes that the configured...

API & webhooks

Setting up the Stripe webhook

Overview A webhook is Stripe's way of reporting back to Kyvento: whenever something happens in Stripe that Kyvento needs...

API & webhooks

Setting up the PayPal webhook

Overview A webhook is PayPal's way of reporting back to Kyvento: whenever something happens in PayPal that Kyvento needs...