Quickstart

1. Create a test key

Log in to the partner portal, open API keys and create a TEST key. Copy it — it is shown only once.

export KL_KEY=kl_test_…

2. Check the key

curl https://api-test-pp.keikkis.fi/v1/me -H "Authorization: Bearer $KL_KEY"

3. See your work types

curl https://api-test-pp.keikkis.fi/v1/work_types -H "Authorization: Bearer $KL_KEY"

Each code comes with the VAT valid today. Use a code as work_type.

4. Invite a worker

curl -X POST https://api-test-pp.keikkis.fi/v1/connections \
  -H "Authorization: Bearer $KL_KEY" -H "Content-Type: application/json" \
  -d '{"external_user_id": "u_1842", "email": "aino@example.com"}'

A new connection is always pending. In production Keikkalasku emails the invitation; you can also show connect_url in your own UI.

5. Preview a gig

curl -X POST "https://api-test-pp.keikkis.fi/v1/gigs?dry_run=true" \
  -H "Authorization: Bearer $KL_KEY" -H "Content-Type: application/json" \
  -d '{
    "external_id": "hv-2026-11-07-001",
    "date": "2026-11-07",
    "title": "Keikka Tavastialla",
    "work_type": "performing_arts",
    "workers": [
      {"external_user_id": "u_1842", "email": "aino@example.com", "amount": "200.00"}
    ]
  }'

dry_run runs every validation and limit and returns the VAT and totals, without saving anything. It answers identically in both environments.

6. Report it

The same request without dry_run creates the gig (201). Sending it again returns the same gig (200); sending the same external_id with different content is 409 idempotency_conflict.

7. Poll the status

curl "https://api-test-pp.keikkis.fi/v1/gigs?updated_since=2026-11-07T00:00:00Z" \
  -H "Authorization: Bearer $KL_KEY"

Keep the latest updated_at you have seen and use it as the next updated_since.

Going live

When Keikkalasku has opened production use, create a PRODUCTION key and change the base URL to https://api-pp.keikkis.fi. Nothing else changes.