Getting started with Maventa ePayslip

Maventa ePayslip is the service Finnish payroll software uses to deliver payslips to employees. Recipients view their payslips in the Maventa ePayslip viewing service, which they reach via a link from their online bank, or receive them as physical letters when electronic delivery is not possible. This page is the entry point for partners building an integration against our API.

REST and SOAP

We run two APIs against the same service:

  • REST API — the current API and the one all new integrations use.
  • SOAP API — the original API, available only to partners who are already integrated against it.

Over time the REST API will replace the SOAP API. Behaviour, business rules, and the underlying payslip XML are the same on both, so existing functionality is preserved. The rest of this page describes the REST API.

Migrating from the SOAP API to the REST API

A short guide for partners moving from the existing SOAP API to the new REST API. Most of the work is on the wire — the underlying behavior, payslip XML schema (v2.0), and business rules are unchanged.

For full per-endpoint usage details (request bodies, examples, response schemas), use the interactive API browser at https://verkkopalkkademo.maventa.fi/api/scalar/v1 or feed https://verkkopalkkademo.maventa.fi/api/openapi/v1.json into your code generator. The reference is hosted on the demo environment; demo and production expose the same API surface, so it applies to both. This guide only describes what is different between SOAP and REST.

Environments

Host
Demo / testhttps://verkkopalkkademo.maventa.fi
Productionhttps://verkkopalkka.maventa.fi

Use the demo host while integrating; switch to the production host when ready to go live. All REST API endpoints sit under the /api/ path of these hosts.


Endpoint mapping

SOAP operationREST endpoint
(none)POST /api/connect/token (new — auth)
SubmitPayslips / SubmitDeliveryBatchPOST /api/v1/batches
GetPayslipProcessingStatusAckGET /api/v1/batches/status
DeletePayslipDELETE /api/v1/payslips
(none)DELETE /api/v1/batches/{batchId}/payslips (new — bulk delete)
ActivateCustomerPayrollContractPOST /api/v1/payers
DeactivateCustomerPayrollContractPOST /api/v1/payers/{vatId}/deactivate
GetContractActiveCustomerIdentifiersGET /api/v1/payers
GetContractActiveCustomerVatIdentifiersGET /api/v1/payers
GetPayerRegistrationStateGET /api/v1/payers/{vatId}
(none)PATCH /api/v1/payers/{vatId} (new — update name / invoicing customer)
GetContractedPayslipTransactionsGET /api/v1/payers/transactions
GetContractedPayslipCustomerCountremoved — use totalPayslipCount from /api/v1/payers/transactions
GetActiveBankBicCodesGET /api/v1/banks/bic-codes

Both SOAP and REST run in parallel — there is no flag day. Migrate one operation at a time and switch traffic at your own pace.


What you have to change in your integration

Authentication: token instead of header credentials

Username + password are no longer in every request’s SOAP header. Instead, call POST /api/connect/token once, cache the returned bearer token, and send it as Authorization: Bearer <token> on every other call.

  • Token lifetime: 1 hour. Cache it.
  • Same credentials as the SOAP service — no new accounts to provision. Treat them as one credential: a leak, rotation, or password change on either side applies to both APIs at once.
  • Token endpoint is rate-limited to 20 requests per 10 minutes, so don’t fetch a new token per call.
  • Repeated wrong-password attempts will temporarily lock the account (same behaviour as SOAP). Contact support if you need a password reset.

Batch upload: multipart instead of base64

ZIP file is sent as raw binary in a multipart/form-data field named zipFile instead of base64-encoded inside <ZipFile>…</ZipFile>. About 25 % less bytes on the wire (base64 expansion goes away).

  • OriginalFileName SOAP header → fileName form field.
  • BatchId is always generated by the server. If you sent your own externally-generated ID before, store the server’s response batchId instead.
  • Convert flag is gone. Payslip XML must be v2.0. If you still produce 1.1, convert before sending.
  • Same ZIP rules: ≤ 100 MB, only .xml and .pdf, optional PDF attachments matched by exact path.

Request and response format: JSON

All non-batch requests use JSON bodies (or query strings) and return JSON responses. The SOAP Acks / Products XML envelopes are replaced with simple JSON arrays and objects — see Scalar UI for the exact shapes.

  • Property names use camelCase.
  • Date-range filters use half-open intervals [start, end). To query all of March 2026: startDate=2026-03-01&endDate=2026-04-01.
  • Status query date range max 24 hours; transactions max 31 days.

Errors: HTTP status codes + ProblemDetails JSON

FaultException<BatchDeliveryFault> is replaced by HTTP status codes plus an RFC 9457 application/problem+json body. The human-readable error reasons in the detail field are the same strings you handle today (BankNotIdentified, NoValidContract, Duplicate, Print delivery requires CountryCode, etc.).

StatusWhen
400Validation error
401Missing / invalid / expired token
404Resource not found, OR cross-tenant access (never an info leak), OR targeting a print-only payslip on a delete endpoint
409Conflict (payer already active; delete matches multiple rows — provide messageId)
429Rate limited (1000 req/min global per client)
500Server error — include the traceId field from the error response body, the timestamp, and the endpoint when contacting support

Dates: UTC with Z

Always serialize as UTC with the Z suffix: 2026-05-04T10:32:11Z. Local-time strings are not accepted.


SOAP fields and operations that no longer exist

A small list so you don’t go hunting for them:

GoneReplacement
GetContractedPayslipCustomerCount operationtotalPayslipCount field in GET /api/v1/payers/transactions
Convert flag and payslip XML 1.1 supportConvert to 2.0 before sending
Client-supplied BatchId on submitServer-generated, returned in response
customerId Guid on deactivationvatId in the URL path
personId, dateOfPayment, receivedDate on DeletePayslipmessageId (or batchId) is now the canonical selector. Capture messageId on submit if you don’t store it today
externalIdentifier on payer activationvatId is the stable key

Migration checklist

  1. Implement a token cache that fetches on startup and refreshes before the 1-hour expiry.
  2. Switch your batch upload from base64-in-XML to multipart form upload. Drop your client-side BatchId — store the server’s response.
  3. Make sure your code records MessageId per submitted payslip — it’s the only stable selector for DELETE /api/v1/payslips.
  4. Switch SOAP XML parsing to JSON for status, payer, transaction, and BIC code responses. The error strings in description / detail are unchanged — map them through.
  5. Switch Activate / Deactivate payload construction to the JSON / URL-path shapes documented in Scalar.
  6. Handle HTTP status codes plus ProblemDetails JSON for error responses.
  7. Ensure all outbound dates are UTC with Z.
  8. Test in our demo environment

Where to find usage details

  • Full REST documentation (concepts, packaging rules, print delivery, payer activation, billing): https://maventaverkkopalkka.fi/verkkopalkan-kayttoonotto/
  • Interactive UI (try every endpoint, generate code samples in your language):
    • Demo: https://verkkopalkkademo.maventa.fi/api/scalar/v1
    • Production: not available — use the demo environment for testing
  • OpenAPI 3 schema (feed into NSwag, openapi-generator, etc.):
    • Demo: https://verkkopalkkademo.maventa.fi/api/openapi/v1.json
    • Production: not available — use the demo schema
  • Support: same channel as for the SOAP API. When reporting an issue, include the traceId field from the error response body, the timestamp, and the endpoint you called.

Close

Environments

Host
Demo / testhttps://verkkopalkkademo.maventa.fi
Productionhttps://verkkopalkka.maventa.fi

All REST API endpoints sit under the /api/ path of these hosts — for example, https://verkkopalkkademo.maventa.fi/api/connect/token. The root of each host serves the employee viewing service, not the API.

Build and test against demo. Move to production once your integration is verified.

API reference

The full API reference, including request and response shapes, examples in curl and several languages, and a button to call every endpoint live, is generated from the API itself and is always up to date. It is hosted on the demo environment; demo and production expose the same API surface, so the reference applies to both.

  • Interactive API browser (Scalar): https://verkkopalkkademo.maventa.fi/api/scalar/v1
  • OpenAPI 3 schema: https://verkkopalkkademo.maventa.fi/api/openapi/v1.json — feed into your code generator (NSwag, openapi-generator, etc.) to produce a typed client in your language.

The rest of this page only covers the things that the API reference does not — concepts, packaging rules, and the print options that live inside the payslip XML.

Quick start in six steps

  1. Request credentials by contacting our sales team via the Contact page. You receive a client_id and client_secret for the demo environment, and separate ones for production.
  2. Get an access token from POST /api/connect/token using the OAuth 2.0 client_credentials grant. Tokens are valid for one hour — cache and reuse them.
  3. Activate the payer (the company on whose behalf you will send payslips) with POST /api/v1/payers. This must be done once per company before any payslips for that company can be accepted.
  4. Submit a payslip batch to POST /api/v1/batches as a multipart/form-data upload containing one ZIP file with the payslip XML(s) and optional PDF attachments.
  5. Poll GET /api/v1/batches/status for processing results. Each payslip is acknowledged with Ok or with a clear error message.
  6. Move to production — change the base URL, swap to your production credentials, and you’re live.

Other operations are available for day-to-day work — deleting payslip(s) when the data needs correcting, updating a payer’s name or invoicing details, deactivating a payer, fetching billing transactions for invoicing your own customers, and listing supported banks. All are listed in the API reference linked above.

The token endpoint is rate-limited to 20 requests per 10 minutes; all other endpoints share a 1000-requests-per-minute limit per client.

Payslip batches

A batch is a single ZIP file containing one or more payslip XML documents (PayslipXML schema 2.0) and any PDF attachments referenced from them. The maximum size is 100 MB. The server returns a batchId in the submit response — store it together with each payslip’s messageId, since those are the keys used by later status and delete calls.

Each payslip declares one or more delivery channels:

CodeChannelWhat happens
01ePayslipDelivered electronically; the employee opens the payslip in the Maventa ePayslip viewing service via a link from their online bank.
02PrintPrinted and mailed as a physical letter.
01 + 02BothBoth of the above for the same payslip.

For ePayslip delivery, the recipient’s bank is identified from the BankCode element or, as a fallback, from the IBAN in BankAccount. The live list of supported banks and their BIC codes is available at GET /api/v1/banks/bic-codes.

Print delivery — only needed if you send physical letters

Print delivery

Print delivery is configured in the payslip XML, so it works the same whether you submit batches via the REST API or the SOAP API.

When a payslip is delivered as a physical letter, the print-specific settings live inside the payslip XML — they are part of the schema rather than separate REST fields.

Recipient address

The recipient block is built from <Delivery><Recipient> and from the print delivery channel’s <DeliveryInfo>. The table below shows where each XML element goes: onto the cover page (the first printed page, visible through the envelope window), into a form field sent to the printing service, or both.

XML elementRequiredCover pagePrinting service fieldEffect
RecipientName.ForeName + SurNameSchemaAddressee window — name linerecipient[name]Name shown on the letter and used as the addressee.
AddressLine (one or more)SchemaAll lines printed in orderrecipient[address] (all lines, joined with a space)All lines are forwarded to the printing service in the order given.
PostalCodeSchemaBelow addressrecipient[post_code]Postal routing.
PostOfficeSchemaBelow postal coderecipient[post_office]City / post office.
CountryCode (ISO 3166-1 alpha-2)Print delivery— (not printed)recipient[country]Tells the printing service whether the letter is domestic or foreign — the routing fee follows this.
Country (full name)Foreign printLast address line for foreign mail; suppressed for Finland / SuomiCover-page only. Write it in the language you want printed on the envelope.
<DeliveryInfo><RecipientTelephone>Optional— (not printed)recipientUsed only for digital-post lookups (helps match the recipient to their OmaPosti account). International format, e.g. +358401234567.
<DeliveryInfo><RecipientEmailAddress>Optional— (not printed)recipient[email]Same digital-post matching as phone.

Sender address

The sender block is built from <Delivery><Sender> and <HeaderData><PartyIdentifications>.

XML elementRequiredCover pagePrinting service fieldEffect
Sender.Name (first non-empty)SchemaReturn-address — name linesender[name]Name shown as the letter’s sender.
Sender.AddressLine (one or more)SchemaAll lines printed in orderCover-page only.
Sender.PostalCodeSchemaBelow addressCover-page only.
Sender.PostOfficeSchemaBelow postal codeCover-page only.
Sender.CountryOptionalSuppressed for Finland / Suomi; printed otherwiseCover-page only. Posti does not require a sender country on outbound mail, so Finnish payers can leave this empty.
<HeaderData><PartyIdentifications> with Authority="OVT" or Authority="EIA"Optional— (not printed)sender[sender_id]Needed for OmaPosti digital routing. The first OVT/EIA entry is used; other authorities (Y-tunnus, VAT, EDI, GLN, …) are deliberately ignored because they would be rejected by the digital route.

The values in the Required column mean:

  • Schema — required by the PayslipXML 2.0 schema. Missing-element payslips fail XSD validation and the whole batch is rejected.
  • Print delivery — required only when the payslip uses print delivery (DeliveryMethodCode = 02 or both). Missing-element payslips are rejected during batch processing with the message ”Print delivery requires CountryCode (ISO 3166-1 alpha-2)”.
  • Foreign print — required only for foreign print delivery (CountryCode ≠ FI). Rejection message: ”Foreign print delivery requires both CountryCode and Country (full name for envelope)”.
  • Optional — not enforced; supplying it just enables additional functionality (e.g. OmaPosti routing, digital-post matching).
Cover-page address layout

Both blocks are rendered in Arial 10 pt into fixed boxes whose dimensions and positions match the printing service’s cover page layout. The printing service publishes the full specification — see the layout design instructions and the cover page template.

BoxLines, in order
SenderName → each AddressLine on its own line → PostalCode + PostOffice joined with a space → Country (only when not Finnish)
RecipientFirst name + surname → each AddressLine on its own line → PostalCode + PostOffice joined with a space → Country (only when not Finnish)

Capacity (in addition to the fixed name + postal/city + country lines):

  • Sender box fits up to 4 AddressLine lines (7 lines total).
  • Recipient box fits up to 5 AddressLine lines (8 lines total).

Wrapping rules:

  • A line that is too wide is wrapped at word boundaries onto the next line, consuming one of the available lines above.
  • A single word wider than the box itself is broken across characters.
  • If the resulting text does not fit vertically into the box, the whole payslip is rejected during batch processing — the address would otherwise be silently clipped on the printed letter. The rejection message names which box (sender or recipient) and asks you to shorten the name, address lines, postal code, city, or country.

If a partner’s data hits the limit consistently, the practical fix is to reduce the number of AddressLine entries — for example, merge ”Long Street Name 12” and ”A 5” into a single AddressLine. Empty <AddressLine></AddressLine> elements are dropped automatically and do not consume a line, so they are safe to leave in.

Letter options (PrintingInfo)

<DeliveryChannel>
  <DeliveryMethodCode>02</DeliveryMethodCode>
  <DeliveryInfo>
    <PrintingInfo>
      <LetterClass>economy</LetterClass>     <!-- economy (default) | priority -->
      <Form>color_print,duplex</Form>        <!-- comma-separated, optional   -->
    </PrintingInfo>
  </DeliveryInfo>
</DeliveryChannel>
SettingValuesDefault
LetterClasseconomy, priorityeconomy
Formcolor_print, duplex (any combination, or omit)Black & white, single-sided

Attachments

PDF attachments referenced from the payslip XML are appended after the payslip pages. They are printed as supplied, so leave at least a 17 mm top margin and 3 mm side and bottom margins in any PDF you reference — otherwise content near the edge can be clipped.

OmaPosti routing

If the recipient has registered for OmaPosti and chosen to receive mail digitally, the letter is delivered to their OmaPosti inbox instead of being physically mailed. Recipients activate the service themselves at posti.fi; we cannot enroll them. The digital lookup uses the sender OVT (see the sender table above) plus, when supplied, the recipient phone and email; without an OVT the letter is still printed and mailed normally, only the digital routing step is skipped.

Close

Activating payers

A payer (the company on whose behalf you are sending payslips) must be activated before payslips for that payer can be submitted: POST /api/v1/payers. The payer record holds the company name, business ID, and an optional separate invoicing company — this is the information used to attribute and invoice the payslips sent on that company’s behalf. Once activated, the payer remains active until you deactivate it. The same endpoint family covers listing, updating, and deactivating payers — see Scalar for shapes.

When a payslip arrives, the payer is identified by the first <PartyIdentification> entry in <HeaderData><PartyIdentifications>. Its value must match the business ID of an activated payer, otherwise the payslip is rejected. Put the business ID first. If you also include an OVT identification (used to enable OmaPosti digital routing for print delivery — see the sender table above), add it as a separate <PartyIdentification> after the business ID; placed before it, the OVT would be treated as the payer ID and no match would be found.

Reporting and billing

Billing depends on the delivery method. Electronic delivery is counted per payslip; print delivery is billed per letter on top of that, separately from the payslip transaction count.

Delivery methodCounted as ePayslip transactionBilled for print
ePayslip only (01)yes
Print only (02)yes
Both (01 + 02)yesyes

The transactions endpoint covers only the ePayslip side: GET /api/v1/payers/transactions returns one row per delivered electronic payslip plus a total count for the period (max 31 days per query). Useful if you want to invoice your own customers from it. Print-only payslips do not appear in this response.

Print is billed per produced letter on the factors that affect production cost:

  • Number of A4 pages (first page plus additional pages)
  • Black & white vs colour
  • Economy vs priority class
  • Domestic (Finland) vs foreign destination

Support

Technical questions, credential requests, and billing enquiries: see the Contact page for the right team and channel.

When reporting an API issue, please include the traceId field from the error response body, the timestamp, and the endpoint you called.