# Errors

Errors are RFC 9457 problem documents (`application/problem+json`). Each has a stable `code`, the `field` concerned (validation errors), a `hint` on how to fix it and a `docs_url`. With several validation errors, all of them are listed under `errors`.

```json
{
  "code": "invalid_amount",
  "docs_url": "https://kumppani-pp.keikkis.fi/docs/errors#invalid_amount",
  "field": "workers[0].amount",
  "hint": "amount must be a string with two decimals, e.g. \"200.00\", greater than zero. It is VAT-excluded.",
  "request_id": "F1a2B3c4D5e6",
  "status": 422,
  "title": "The amount is not valid.",
  "type": "https://kumppani-pp.keikkis.fi/docs/errors#invalid_amount"
}
```

| Code | HTTP | Meaning | Fix |
|---|---|---|---|
| <a id="api_disabled"></a>`api_disabled` | 503 | The Partner API is temporarily switched off. | The API has been switched off by Keikkalasku. Retry later or contact info@keikkalasku.fi. |
| <a id="missing_api_key"></a>`missing_api_key` | 401 | No API key was sent. | Send the key in the Authorization header: `Authorization: Bearer kl_test_…`. |
| <a id="invalid_api_key"></a>`invalid_api_key` | 401 | The API key is not valid. | Check that the whole key was copied. Keys are shown only once; create a new one in the partner portal if needed. |
| <a id="key_revoked"></a>`key_revoked` | 401 | The API key has been revoked. | Create a new key in the partner portal and stop using this one. |
| <a id="wrong_environment"></a>`wrong_environment` | 401 | The key belongs to the other API environment. | kl_test_ keys work only on api.test.keikkalasku.fi and kl_live_ keys only on api.keikkalasku.fi. |
| <a id="partner_suspended"></a>`partner_suspended` | 403 | The partner account is suspended. | Contact info@keikkalasku.fi. |
| <a id="production_not_enabled"></a>`production_not_enabled` | 403 | Production use has not been opened for this partner. | Keikkalasku opens production use once the agreement, DPA and customer due diligence are complete. Use a kl_test_ key until then. |
| <a id="ip_not_allowed"></a>`ip_not_allowed` | 403 | The request came from an address outside the key's IP allowlist. | Send live requests from an allowed address, or ask Keikkalasku to update the allowlist. |
| <a id="insufficient_scope"></a>`insufficient_scope` | 403 | The API key does not have the scope this endpoint needs. | Create a key with the required scope in the partner portal. |
| <a id="rate_limited"></a>`rate_limited` | 429 | Too many requests. | Wait the number of seconds in the Retry-After header and retry. |
| <a id="unknown_host"></a>`unknown_host` | 404 | This host does not serve the API. | Use https://api.keikkalasku.fi (live) or https://api.test.keikkalasku.fi (test). |
| <a id="not_found"></a>`not_found` | 404 | The resource does not exist. | Check the id. Gigs can be fetched by our id (gig_…) or your external_id; connections by con_… or your external_user_id. |
| <a id="invalid_json"></a>`invalid_json` | 400 | The request body is not valid JSON. | Send a JSON object with `Content-Type: application/json`. |
| <a id="invalid_parameter"></a>`invalid_parameter` | 400 | A query parameter is invalid. | See the endpoint's parameters in the API reference. |
| <a id="missing_field"></a>`missing_field` | 422 | A required field is missing. | Add the field named in `field`. |
| <a id="invalid_field"></a>`invalid_field` | 422 | A field has an invalid value. | See the field's format in the API reference. |
| <a id="invalid_email"></a>`invalid_email` | 422 | The email address is not valid. | Send a full email address, e.g. "worker@example.com". |
| <a id="invalid_date"></a>`invalid_date` | 422 | The date is not valid. | Send an ISO 8601 date, e.g. "2026-11-07". |
| <a id="date_in_future"></a>`date_in_future` | 422 | The gig date is in the future. | Report a gig on or after the day it was performed. |
| <a id="date_too_old"></a>`date_too_old` | 422 | The gig date is more than 12 months ago. | Gigs older than 12 months cannot be invoiced through the API. Contact info@keikkalasku.fi. |
| <a id="invalid_amount"></a>`invalid_amount` | 422 | The amount is not valid. | amount must be a string with two decimals, e.g. "200.00", greater than zero. It is VAT-excluded. |
| <a id="amount_over_limit"></a>`amount_over_limit` | 422 | The amount is above the partner's per-worker limit. | Split the amount or ask Keikkalasku to raise the limit. |
| <a id="too_many_workers"></a>`too_many_workers` | 422 | The gig has more workers than the partner's limit allows. | Split the gig or ask Keikkalasku to raise the limit. |
| <a id="no_workers"></a>`no_workers` | 422 | The gig has no workers. | Send at least one worker in `workers`. |
| <a id="duplicate_worker"></a>`duplicate_worker` | 422 | The same worker appears twice in one gig. | Send each worker once, with their total share for the gig. |
| <a id="worker_identifier_missing"></a>`worker_identifier_missing` | 422 | A worker has neither external_user_id nor email. | Identify each worker with external_user_id, email, or both. |
| <a id="unknown_worker"></a>`unknown_worker` | 422 | No connection exists for this worker. | Create the connection first (POST /v1/connections) or include the worker's email, which sends an invitation. |
| <a id="daily_cap_exceeded"></a>`daily_cap_exceeded` | 422 | The gig would take the partner over its daily euro cap. | Retry tomorrow or ask Keikkalasku to raise the cap. |
| <a id="work_type_required"></a>`work_type_required` | 422 | No work type was given and the partner has no default. | Send work_type on the gig or on each worker. GET /v1/work_types lists the codes you may use. |
| <a id="work_type_not_allowed"></a>`work_type_not_allowed` | 422 | The work type is not on the partner's list. | Use one of the codes from GET /v1/work_types, or ask Keikkalasku to allow this one. |
| <a id="work_type_not_available_on_date"></a>`work_type_not_available_on_date` | 422 | The work type has no valid VAT treatment on the gig date. | Check the gig date. GET /v1/work_types shows when each work type is valid. |
| <a id="work_type_retired"></a>`work_type_retired` | 422 | The work type has been retired. | Use the replacement code named in the hint. |
| <a id="idempotency_conflict"></a>`idempotency_conflict` | 409 | A gig with this external_id already exists with different content. | external_id is the idempotency key: resend the identical gig, or use a new external_id for a different gig. |
| <a id="already_invoiced"></a>`already_invoiced` | 409 | The gig cannot be cancelled: a worker has already been invoiced. | Contact info@keikkalasku.fi to credit an invoice that has been sent. |
