Test mode

An empty copy of your account to build your integration end to end without filing anything with the Ministry, without charges and without emailing real guests.

What it is

Every account has a test environment: a twin account with its own bookings, properties, API keys and webhooks. The API is the same —same routes, same fields, same events and same statuses— and only the very end of the path changes: filing is answered by a simulator inside Kinapi instead of the Spanish Ministry of the Interior.

It exists because the Ministry does not issue test credentials to each landlord: its test environment (PRE) is for whoever builds the service, not for their customers. Without test mode, testing an integration with Kinapi meant testing it against production.

Turning it on

In the panel, open the account menu (top right) and choose Switch to test mode. It is also under Settings → API. While you are in test mode an amber strip says so at the top and the panel only shows test data; to leave, choose Back to live.

The first time, the environment is created empty, with your branding, your guest notification settings and your custom email texts copied from live. Webhooks are not copied —you set them up separately with their own URL, so test events never reach your production receiver— and neither are Ministry credentials, which are not needed here.

The mode belongs to your browser, not to the account: your team can keep working live while you test. Signing out or closing the browser takes you back to live.

Test keys

With test mode on, go to Settings → API and create a key. It starts with kinapi_test_ and only sees test data: it cannot read or change anything live, because it belongs to another account. Use it exactly like a live key, including with the MCP server, which also has tools to simulate the Ministry’s answer, read the test inbox and empty the environment.

To know which environment a key talks to, check livemode in GET /settings. Do it when your integration starts: it is how a test key never ends up in your production configuration by mistake, or the other way round.

curl
curl https://api.kinapi.es/api/v1/settings \
  -H "ocp-apim-subscription-key: kinapi_test_…"
200 OK
{
  "livemode": false,
  "ses": { "submitRequiresSignature": false },
  …
}

Getting a report accepted and one rejected

The flow is the live one: you create the property and the booking, guests register from their link (or you add them through the API) and the report goes out on the night of arrival, or right away with POST /checkins/{reservationId}/ses. The difference is that the "Ministry" answers in seconds instead of on the hourly poll: you get ses.submitted followed by ses.confirmed or ses.error.

You decide the outcome in two ways: by asking for it explicitly (next section) or through the data you send, which also works when testing from the guest link or the panel without the API:

Through the data
FieldTypeDescription
apellido1: "RECHAZO"guestThat guest is rejected when the batch is checked, with the same message format the Ministry uses ("Valor incorrecto para el campo Primer apellido"). The other guests on the booking are accepted. The booking ends up in error and you get ses.error with phase "poll".
codigoEstablecimiento: "RECHAZO"propertyThe whole submission is rejected on the spot, like a header error. Nothing is sent and you get ses.error with phase "submit".
anything else—Accepted: every guest gets a test communication code and you get ses.confirmed.

Case and accents do not matter: "Rechazo" or "rechazó" count the same. Test batches and communication codes start with PRUEBA-, and in GET /checkins/{reservationId}/ses every submission carries env: "test".

Forcing the Ministry’s answer

POST/checkins/{reservationId}/ses/simulate

Files the report now, like POST /checkins/{reservationId}/ses, and makes the simulated Ministry answer whatever you ask, without touching the booking’s data. Everything else happens as it would live: the booking status, the submission detail and the webhooks. In the panel it is on each booking’s page: “Simulate the Ministry’s answer”.

curl
curl -X POST https://api.kinapi.es/api/v1/checkins/R-2026-0412/ses/simulate \
  -H "ocp-apim-subscription-key: kinapi_test_…" \
  -H "content-type: application/json" \
  -d '{ "result": "rejected", "rejection": "numeroDocumento" }'
200 OK
{
  "simulated": true,
  "result": "rejected",
  "lote": "PRUEBA-3f9a1c2e-DD",
  "status": "error"
}
result
FieldTypeDescription
acceptedoutcomeEvery guest in the submission gets a communication code. Webhooks: ses.submitted and ses.confirmed.
rejectedoutcomeThe guests are rejected with the rejection you choose in rejection (default: first surname). With guestIds only those are rejected and the rest are accepted. Webhooks: ses.submitted and ses.error with phase "poll".
submit_erroroutcomeThe whole submission is refused on the spot, like a header error. Webhook: ses.error with phase "submit".
blockedoutcomeThe booking is held as if your account were suspended for non-payment. Webhook: ses.blocked. The next nightly run releases it, as when an account settles its payment.
rejection: the rejection catalogue
FieldTypeDescription
apellido1campoInvalido“Valor incorrecto para el campo Primer apellido”, with the surname you sent.
nombrecampoInvalido“Valor incorrecto para el campo Nombre”.
numeroDocumentocampoInvalido“Valor incorrecto para el campo NIF / NIE / Pasaporte”, depending on the document type.
fechaNacimientocampoInvalido“Valor incorrecto para el campo Fecha de nacimiento”.
documentoDuplicadodocumentoDuplicado“No se admiten datos repetidos de personas con el mismo rol en una comunicación”.
numPersonasnumPersonasNoCoincide“El número de personas ingresado no coincide con los detalles proporcionados”.
codigoPostalcodigoPostalInvalido“Un código postal ha de ser un número de longitud 5 y existir dentro del territorio español”.
sinClasificar—A rejection Kinapi cannot translate: the message comes in errores and rechazos is empty. Use it to test the case your integration cannot recognise.

The messages copy the Ministry’s real format, in Spanish, and carry the values you sent, so they arrive in errores just like a real one and Kinapi translates them in rechazos with the code in the second column. It needs the same as a real submission: guests not yet filed, an establishment code on the property and, if your account requires it, the signed contract. If something is missing it answers 409 not_ready with the reason. With a live key it answers 403 test_mode_only, like every other endpoint on this page.

Test inbox

GET/test/emails

Every email test mode generates is kept for 30 days in an inbox, whether it went out to your team or not: the guest invitation, the reminder, the copy of the signed contract, the registration-complete notice… So you see what any guest would have received without putting your own email on bookings. In the panel it is in the test-mode strip, “Test inbox”; over the API, newest first:

200 OK
{
  "results": [
    {
      "id": "6b1f…",
      "to": "maria@example.com",
      "subject": "María, complete your registration for Casa Malia",
      "kind": "guest_invitacion",
      "delivered": false,
      "attachments": [],
      "text": "Hi María, … https://www.kinapi.es/c/aGb3xKf9… …",
      "html": "<!doctype html>…",
      "createdAt": "2026-10-09T09:12:44.120Z"
    }
  ]
}

delivered says whether it really went out (the address belongs to your team) or stayed in the inbox. An invitation’s registration link is in text: your automated tests can read it from there and walk through the guest registration end to end.

Emptying the environment and automatic deletion

POST/test/reset

Deletes every test booking with everything hanging from it: guests, submissions, signatures, test inbox and webhook deliveries. Properties, owners, templates, house rules, API keys, the webhook and settings stay, so you can call it before each test run to always start the same way. No webhook is sent for what it deletes. In the panel it is under Settings → Account, with test mode on.

200 OK
{ "reset": true, "checkins": 12, "emails": 30 }

On top of that, test bookings are deleted automatically 30 days after their check-out date, and inbox emails after 30 days. Unlike live —where a filed booking can only be cancelled—, nothing here ever reached the Ministry and there is nothing to keep.

What changes compared with live

MinistryNothing leaves Kinapi: not the report itself, not the batch check, not the annulment when a booking is cancelled or its dates change. No Ministry credential is used.
BillingNothing is billed and nothing uses up your free bookings. GET /usage with a test key is always zero.
EmailsThey only reach your team: the signup email, panel users and the addresses set in your notifications, always with [TEST] in front of the subject. Nothing is sent to any other address, although for the rest of the system it counts as sent. All of them, sent or not, stay in the test inbox.
WebhooksThe same events, with livemode: false in the body and the header X-Kinapi-Livemode: false. You set them up separately, inside test mode.
ContractIt is generated and signed the same way, but it is not archived in immutable storage or timestamped by a timestamping authority: a test contract is evidence of nothing. GET …/evidence returns an empty list.
Guest linkThe registration page says at the top, in the guest’s language, that it is a test environment.
DeletionBookings are deleted automatically 30 days after check-out, and you can empty them all whenever you want. Live, a filed booking is never deleted.
LimitsThe same daily limits as live.

Test mode cannot check what only the Ministry knows: that your establishment code exists or that your credentials are valid. Before going live, test the connection with your real credentials from Settings → SES Hospedajes.

Going live

There is nothing to migrate: test mode is a separate account and nothing you create in it moves to live. Create a live key (without the test prefix), add your properties there with their real establishment code and set up your live webhook. If you close your account, its test environment is deleted with it.