# Invoicing

NOTE This is work in progress so please ask questions and give feedback to us, so we can improve the documentation and the API through the Slack channel you've been/will be invited to.

# Invoice Flow

# Prerequisites

To use the invoice creation endpoint you need an OAUTH token. See oauth-flow, preferrably Personal Access Token so your users don't have to do the authentication flow every time they are sending an invoice.

# Create invoice request

A POST request to the /v2/invoices endpoint creates a new invoice

Row unit prices need to be expressed in the money format .

# Fields:

  • client_type
    • COMPANY or PERSON
  • email
    • required if delivery method is EMAIL
  • business_id
    • required when client_type is COMPANY
  • company_name
    • required when client_type is COMPANY, limited to 35 characters
  • contact_person
    • required, max 35 characters
  • description
    • Required when notify_overdue_and_automatic_debt_collection is set true. The description of the done work must
    • max: 300 characters be on the invoice. Just "work" or list of targets/addresses is not descriptive enough. The description has to tell both the client and the person processing the invoice what work is being invoiced.
  • language
    • FI or SV or EN
  • notify_overdue_and_automatic_debt_collection
    • When true UKKO.fi takes care of invoice follow-up, overdue notices and debt collection.
  • vat_rate
    • The vat_rate parameter is required to be "VAT_REGULAR_255" for 25,5% vat OR "VAT_CONSTRUCTION_REVERSE_CHARGE" for the construction industry's reverse-charge vat.
  • vat_rate_explanation
    • Required when vat_rate is not VAT_REGULAR_255
  • occupation_id
    • The line of work being invoiced
    • NOTE: This may require a mapping between your line of work lists and UKKO.fi lists.
    • NOTE: Also note that jobs requiring permits need to be handled with great scrutiny on our end as some work are not allowed to be invoiced through UKKO.fi.
    • For getting occupation ids see the Getting a list of occupations for the invoice section.
  • term_of_payment
    • minimum 14 days for person client
    • minimum 7 days for company client when notify_overdue_and_automatic_debt_collection is false, otherwise 14 days
  • delivery_method
    • MAIL, EMAIL, EINVOICE allowed for companies
  • einvoice_address
    • required if the delivery_method is set to EINVOICE
  • zip_code
    • required
  • street_address1
    • required
  • country
    • required
  • user_confirmed_work_in_finland is required to be true if the client country is not FI.
  • clients_reference
    • optional, max 35 characters

# Invoice Rows, key rows

Array of objects with properties:

  • short_description
    • Required, max 50 characters
  • quantity
    • 1 or more
  • unit_price
    • Money object and as fractions of cents for extra precision, ie 5 EUR * 1000 = 5000 fractions of cents
  • discount_sum
  • discount_percent
    • Numeric percentage of a discount, e.g. 10 for 10%
    • Cannot be used with discount_sum
  • contains_vat
    • When true invoice gets calculated so that the unit price contains the VAT
  • unit_type
    • is PIECE or HOUR
  • start_date start of the work period in the defined date time format
  • end_date end of the work period in the defined date time format
# Unit_price money values

Although we're handling most of the money values as integer cents, the invoice.rows.[...].unit_price is handled as fractions of cents, e.g. 5 EUR * 1000 = 5 000 as there are cases where the additional precision is required for this field.

{
  "amount": "5000000",
  "currency": "EUR"
}

# Example request:

{
  "company_name": "Example Company Oy",
  "business_id": "6811327-1",
  "einvoice_address": "123",
  "contact_person": "John Doe the 3rd",
  "client_type": "COMPANY",
  "language": "FI",
  "street_address1": "Testitie 5",
  "zip_code": "00100",
  "city": "Helsinki",
  "country": "FI",
  "email": "invoice-receiver@ukko.fi",
  "clients_reference": "123654",
  "description": "Example work description",
  "occupation_id": 82,
  "notify_overdue_and_automatic_debt_collection": true,
  "vat_rate": "VAT_REGULAR_255",
  "delivery_method": "EMAIL",
  "rows": [
    {
      "short_description": "Description of the work that was done",
      "quantity": "10.00",
      "discount_sum": null,
      "discount_percent": null,
      "unit_price": {
        "amount": "20000",
        "currency": "EUR"
      },
      "contains_vat": true,
      "unit_type": "PIECE",
      "start_date": "2023-01-01T00:00:00+02:00",
      "end_date": "2023-01-15T00:00:00+02:00"
    }
  ]
}

# Example response:

{
  "data": {
    "id": 1,
    "company_name": "string",
    "contact_person": "string",
    "client_type": "COMPANY",
    "language": "FI",
    "email": "user@example.com",
    "street_address1": "string",
    "zip_code": "string",
    "city": "string",
    "country": "string",
    "business_id": "string",
    "clients_reference": "string",
    "description": "string",
    "occupation": {
      "id": 12,
      "code": "96021",
      "title_fi": "Parturikampaamo",
      "title_en": "Hairdressing activities",
      "description_fi": "Kampaaja ja parturitoiminta, hiuslisäkkeet",
      "description_en": "Hairdressing and barber services, hair extensions"
    },
    "user_confirmed_work_in_finland": false,
    "vat_rate": "VAT_REGULAR_255",
    "term_of_payment": 14,
    "due_date": "2023-01-15",
    "reference_number": "1234567",
    "barcode": "4301237300012305600120000000000000000000012345674200213",
    "created_at": "2023-01-01",
    "rows": [{
      "id": 1,
      "invoice_id": 1,
      "short_description": "string",
      "start_date": "string",
      "end_date": "string",
      "quantity": 1,
      "unit_type": "PIECE",
      "unit_price": {
        "amount": "125500",
        "currency": "EUR"
      },
      "contains_vat": true,
      "total_sum_with_vat": {
        "amount": "12550",
        "currency": "EUR"
      },
      "total_sum_without_vat": {
        "amount": "10000",
        "currency": "EUR"
      },
      "vat_percent": 0,
      "vat_sum": {
        "amount": "2550",
        "currency": "EUR"
      },
      "discount_type": null,
      "discount_percent": null,
      "discount_sum": null,
      "total_sum_without_vat_before_discount": {
        "amount": "10000",
        "currency": "EUR"
      },
      "total_sum_with_vat_before_discount": {
        "amount": "12550",
        "currency": "EUR"
      },
      "row_type": null
    }],
    "can": {
          "create": {
            "allowed": true
          },
          "send": {
            "allowed": false,
            "reasons": [
              "terms_of_service_not_signed",
              "strong_identification_missing"
            ]
          }
        }
  }
}

More details on the different request and return parameters can be found in the API documentation.

NOTE: If there are other can.send.reasons than terms_of_service_not_signed, you'll need to tell the user to update their UKKO.fi account settings in the https://app.ukko.fi/settings page.

# Getting a list of occupations for the invoice

To get a list of occupations you can use the occupations API. The occupations are available with the following language codes: fi, and en.

# Example Request:

GET https://[UKKO_HOST]/v2/occupations?lang=en

# Example response

  "Vehicle Maintenance": [
    {
      "id": 82
      "code": "62020"
      "title": "Motorcycle maintenance and repair"
      "description": "Description of the occupation in English"
    }
    {
      "id": 83
      "code": "74300"
      "title": "Repair of tyres"
      "description": "Description of the occupation in English"
    }
  ]
  "Information Technology": [
    {
      "id": 85
      "code": "96021"
      "title": "Repair of computers and peripheral equipment"
      "description": "Description of the occupation in English"
    }
  ]

# Showing Terms of Service check

To allow for invoicing a user has to have accepted the UKKO.fi terms of service. If the invoice response has can.send.allowed = false and `can.send.reasons[] = 'terms_of_service_not_signed', you'll need to show the user the current terms of service as defined below.

# Example request

GET https://[UKKO_HOST]/v2/system/service-agreements/terms-of-service?language=fi

To get the current terms of service you can use the terms of service API. The terms of services are available with the following language codes: fi, and en.

# Example response

{
  "data": {
    "terms": "<h2>UKKO.fi-palvelun k&auml;ytt&ouml;ehdot</h2>...",
    "type": "terms-of-service",
    "language": "fi",
    "published_at": "2020-03-03T12:44:30+02:00"
  }
}

# Accepting Terms of Services

After showing the user the TOS and user accepting it. You need to send three POST request to the following endpoint to accept the terms of service.

# Example request

POST /me/service-agreement/has-confirmed-no-business-id/accept
POST /me/service-agreement/terms-of-service/accept
POST /me/service-agreement/industry-restrictions/accept

For API Partners the agreements has-confirmed-no-business-id and industry-restrictions doesn't need to be shown as they are either in the phasing out stage or needs to be added to your own Terms Of Service documents.

Please consult your service agreement with UKKO.fi for more details.

# Sending a created invoice to processing

# Send to processing request

If the Create Invoice response contains can.send.allowed as true, you can send the invoice to admin processing.

A POST request to /v2/invoices/{invoice_id}/processing sends to specified invoice to UKKO.fi administration for processing. The endpoint returns 204 http-status code on success.

Only an invoice that is in the DRAFT state can be sent to processing. Newly created invoices and rejected invoices are in the DRAFT state.

# Example request

POST https://[UKKO_HOST]/v2/invoices/12345/processing`

# What Happens If the Sent Invoice Gets Rejected

One of the benefits of using UKKO.fi is that our trained financial admins manually check and validate most of the invoices before sending the invoices to the client. This means that the invoice may get rejected (sent back to the user for fixes or for additional information) with an explanation message, why the invoice was rejected/discarded.

This means you need to currently poll about the invoice status changes to see whether it went TO SENT or REJECTED state. If it went to REJECTED, then the user needs to make the necessary changes and send the invoice to processing again.

You can read the rejection note contents from invoice response rejection_note.message field.

Later on there will be webhooks for getting updated about invoice status changes and rejects.

# What happens after the invoice sent to processing?

You can ask UKKO.fi to mark the invoice as paid. After that you can see the generated calculated salary in UKKO.fi SPA.

# Policy failures

NOTE: If these are not clear enough, please consult UKKO.fi API support for better descriptions:

  • terms_of_service_not_signed
  • missing_country_of_living
  • country_of_living_not_allowed
  • ssn_missing
  • strong_identification_missing
  • strong_identification_expired
  • too_young
  • guardians_approval_required
  • nationality_not_set
  • has_no_valid_id_document