Invoices | Set Invoice

Invoice Creation Endpoint

This document provides guidance on using the API to create a new invoice for an existing customer through the set_invoice endpoint, optionally sending it through email, SMS and e-invoice in the same call. The response returns the created invoice with its calculated totals, VAT rows per VAT rate, the public payment link, and a per-channel sent/error confirmation.

API ENDPOINT FOR INVOICE CREATION

To create an invoice, you provide your API credentials, the ID of an existing customer, the invoice lines with ALL of their details (a product id alone is never enough — the exact prices you send are the prices invoiced), optional invoice fields (dates, note) and optionally the sending methods. The invoice is created as Open under your own account, exactly like an invoice sent from the system.

Endpoint and Method

  • Endpoint: https://easycms.fi/public_api/set_invoice/
  • Method: POST

Parameters | Payload

API Authentication Parameters

  • TOKEN (api_key): Your unique API key for authentication. How to Create API Credentials.
  • username: Your login username.
  • password: Your login password.
  • account: Your specific account ID.

Invoice Creation Parameters

  • customer_id (required, integer): The ID of an EXISTING customer. New customers must be registered first via the existing customer API calls (e.g. set_customer_register) — passing a customer id that does not exist returns the customer_not_found... error.
  • invoice_lines (required, array or JSON string, max 100 lines): The invoice lines. Each line carries ALL of its own details — passing only a product id is never enough:
    • invoice_lines[n][title] (required, string): Line title shown on the invoice (alias name accepted).
    • invoice_lines[n][quantity] (required, number > 0): Quantity.
    • invoice_lines[n][unit_price] (required, number >= 0): Unit price NET, excluding VAT. The exact value you pass is the value invoiced — the system never recalculates it from the product catalog.
    • invoice_lines[n][vat_percent] (required, number): VAT percent that must exist in the account's VAT table (e.g. 25.5, 24, 14, 10, 0). The response lists the valid values when an unknown percent is sent.
    • invoice_lines[n][pid] (optional, integer): Existing product id for product linkage. All other details must still be sent; the pid alone is never used for prices or discounts.
    • invoice_lines[n][discount] (optional, number 0-100): Line discount percent, locked as a manual discount.
    • invoice_lines[n][line_note] (optional, string): Note printed on this invoice line.
    • invoice_lines[n][unit] (optional): Unit id or name from the account's unit list (default unit when omitted).
    • invoice_lines[n][product_code] (optional, string): Your product code for the line.
  • invoice_date (optional): Invoice date, Y-m-d or dd.mm.YYYY (default: now).
  • due_date (optional): Due date, Y-m-d or dd.mm.YYYY (default: invoice date + the account/customer payment period).
  • delivery_date (optional): Delivery date (default: invoice date).
  • note (optional, string): Invoice note printed on the invoice.
  • title (optional, string): Invoice title (default: the localized word for invoice).
  • send_methods (optional, array or comma-separated list): One or more sending channels: email, sms, einvoice. When present, the invoice is sent through EVERY listed channel in the same call and each channel returns its own sent/error result.
  • email (optional): Email recipient override for the email channel. Default resolution: customer's billing email, then the customer's main email.
  • email_template_id (optional, integer): Email template id (default: the account's invoice template).
  • phone_full (optional): SMS recipient override (E.164 format, e.g. 358401234567). Default resolution: customer's billing phone, then the customer's main phone.
  • phone_prefix (optional): SMS recipient country prefix (used with phone_full).
  • sms_template_id (optional, integer): SMS template id (default: the account's invoice template).

Notes:

  • Every invoice created through the API is created as Open (status open) under YOUR account's API credentials — exactly like an invoice sent from the system — and appears in the invoice list with a real per-channel sent stamp for every successful send.
  • Totals, VAT rows and the payment reference/barcode are calculated by the platform's original invoice engine; the response returns the calculated VAT breakdown per VAT rate.
  • Email recipients are verified by the platform's email-existence gate before anything is sent (billing-list + verification API).
  • E-invoice requires the sender's business id + e-invoice bank account and the customer's e-invoice address (OVT identifier + operator); missing prerequisites return a clear per-channel error.
  • There is no duplicate-submission protection: do NOT blindly retry a timed-out create call (it would create a second invoice).

Handling Responses

After making the POST request, handle the API responses accordingly. The response contains the created invoice object (identification, dates, status, totals, vat_rows, lines, sent_log, customer, note, invoice_url, barcode) and, when send_methods was sent, a send_results object with one {sent: 1|0, error} entry per requested channel. A failed channel never aborts the call — the invoice stays created and open, and the error tells you which channel to retry.



Call Examples in Different Languages


curl -X POST 'https://easycms.fi/public_api/set_invoice' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&customer_id=361&invoice_lines%5B0%5D%5Btitle%5D=Consulting+hours&invoice_lines%5B0%5D%5Bquantity%5D=2.5&invoice_lines%5B0%5D%5Bunit_price%5D=100&invoice_lines%5B0%5D%5Bvat_percent%5D=24&invoice_lines%5B0%5D%5Bdiscount%5D=10&invoice_lines%5B0%5D%5Bpid%5D=4475&invoice_lines%5B0%5D%5Bline_note%5D=Phase+1&invoice_lines%5B0%5D%5Bproduct_code%5D=CONS-1&invoice_lines%5B1%5D%5Btitle%5D=Hosting+service&invoice_lines%5B1%5D%5Bquantity%5D=1&invoice_lines%5B1%5D%5Bunit_price%5D=80.05&invoice_lines%5B1%5D%5Bvat_percent%5D=25.5&due_date=2026-10-20¬e=Thank+you+for+your+business%21&send_methods%5B0%5D=email&send_methods%5B1%5D=sms&send_methods%5B2%5D=einvoice'

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => "https://easycms.fi/public_api/set_invoice",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => http_build_query(['username' => 'USERNAME', 'password' => 'PASSWORD', 'account' => 'ACCOUNT_ID', 'customer_id' => '361', 'invoice_lines[0][title]' => 'Consulting hours', 'invoice_lines[0][quantity]' => '2.5', 'invoice_lines[0][unit_price]' => '100', 'invoice_lines[0][vat_percent]' => '24', 'invoice_lines[0][discount]' => '10', 'invoice_lines[0][pid]' => '4475', 'invoice_lines[0][line_note]' => 'Phase 1', 'invoice_lines[0][product_code]' => 'CONS-1', 'invoice_lines[1][title]' => 'Hosting service', 'invoice_lines[1][quantity]' => '1', 'invoice_lines[1][unit_price]' => '80.05', 'invoice_lines[1][vat_percent]' => '25.5', 'due_date' => '2026-10-20', 'note' => 'Thank you for your business!', 'send_methods[0]' => 'email', 'send_methods[1]' => 'sms', 'send_methods[2]' => 'einvoice']),
  CURLOPT_HTTPHEADER => array("Authorization1: TOKEN"),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;

import requests
url = "https://easycms.fi/public_api/set_invoice"
headers = {"Authorization1": "TOKEN"}
payload = {'username': 'USERNAME', 'password': 'PASSWORD', 'account': 'ACCOUNT_ID', 'customer_id': '361', 'invoice_lines[0][title]': 'Consulting hours', 'invoice_lines[0][quantity]': '2.5', 'invoice_lines[0][unit_price]': '100', 'invoice_lines[0][vat_percent]': '24', 'invoice_lines[0][discount]': '10', 'invoice_lines[0][pid]': '4475', 'invoice_lines[0][line_note]': 'Phase 1', 'invoice_lines[0][product_code]': 'CONS-1', 'invoice_lines[1][title]': 'Hosting service', 'invoice_lines[1][quantity]': '1', 'invoice_lines[1][unit_price]': '80.05', 'invoice_lines[1][vat_percent]': '25.5', 'due_date': '2026-10-20', 'note': 'Thank you for your business!', 'send_methods[0]': 'email', 'send_methods[1]': 'sms', 'send_methods[2]': 'einvoice'}
response = requests.post(url, headers=headers, data=payload)
print(response.text)

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://easycms.fi/public_api/set_invoice"))
    .headers("Authorization1", "TOKEN")
    .POST(HttpRequest.BodyPublishers.ofString("username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&customer_id=361&invoice_lines%5B0%5D%5Btitle%5D=Consulting+hours&invoice_lines%5B0%5D%5Bquantity%5D=2.5&invoice_lines%5B0%5D%5Bunit_price%5D=100&invoice_lines%5B0%5D%5Bvat_percent%5D=24&invoice_lines%5B0%5D%5Bdiscount%5D=10&invoice_lines%5B0%5D%5Bpid%5D=4475&invoice_lines%5B0%5D%5Bline_note%5D=Phase+1&invoice_lines%5B0%5D%5Bproduct_code%5D=CONS-1&invoice_lines%5B1%5D%5Btitle%5D=Hosting+service&invoice_lines%5B1%5D%5Bquantity%5D=1&invoice_lines%5B1%5D%5Bunit_price%5D=80.05&invoice_lines%5B1%5D%5Bvat_percent%5D=25.5&due_date=2026-10-20¬e=Thank+you+for+your+business%21&send_methods%5B0%5D=email&send_methods%5B1%5D=sms&send_methods%5B2%5D=einvoice"))
    .build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

const https = require('https');
const data = new URLSearchParams({ 
  "username": "USERNAME",
  "password": "PASSWORD",
  "account": "ACCOUNT_ID",
  "customer_id": "361",
  "invoice_lines[0][title]": "Consulting hours",
  "invoice_lines[0][quantity]": "2.5",
  "invoice_lines[0][unit_price]": "100",
  "invoice_lines[0][vat_percent]": "24",
  "invoice_lines[0][discount]": "10",
  "invoice_lines[0][pid]": "4475",
  "invoice_lines[0][line_note]": "Phase 1",
  "invoice_lines[0][product_code]": "CONS-1",
  "invoice_lines[1][title]": "Hosting service",
  "invoice_lines[1][quantity]": "1",
  "invoice_lines[1][unit_price]": "80.05",
  "invoice_lines[1][vat_percent]": "25.5",
  "due_date": "2026-10-20",
  "note": "Thank you for your business!",
  "send_methods[0]": "email",
  "send_methods[1]": "sms",
  "send_methods[2]": "einvoice",
}).toString();
const options = {
  hostname: 'easycms.fi',
  path: '/public_api/set_invoice',
  method: 'POST',
  headers: {
    'Authorization1': 'TOKEN',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': data.length
  }
};
const req = https.request(options, (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => { console.log(data); });
});
req.on('error', (e) => { console.error(e); });
req.write(data);
req.end();

import React, { useEffect, useState } from 'react';
function App() {
  const [responseText, setResponseText] = useState('');
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://easycms.fi/public_api/set_invoice', {
          method: 'POST',
          headers: {'Authorization1': 'TOKEN', 'Content-Type': 'application/x-www-form-urlencoded'},
          body: new URLSearchParams({"username": "USERNAME", "password": "PASSWORD", "account": "ACCOUNT_ID", "customer_id": "361", "invoice_lines[0][title]": "Consulting hours", "invoice_lines[0][quantity]": "2.5", "invoice_lines[0][unit_price]": "100", "invoice_lines[0][vat_percent]": "24", "invoice_lines[0][discount]": "10", "invoice_lines[0][pid]": "4475", "invoice_lines[0][line_note]": "Phase 1", "invoice_lines[0][product_code]": "CONS-1", "invoice_lines[1][title]": "Hosting service", "invoice_lines[1][quantity]": "1", "invoice_lines[1][unit_price]": "80.05", "invoice_lines[1][vat_percent]": "25.5", "due_date": "2026-10-20", "note": "Thank you for your business!", "send_methods[0]": "email", "send_methods[1]": "sms", "send_methods[2]": "einvoice"}).toString()
        });
        const data = await response.text();
        setResponseText(data);
      } catch (error) {
        console.error(error);
      }
    };
    fetchData();
  }, []);
  return (
{responseText}
); } export default App;

// Kotlin example requires using a third-party library like OkHttp for POST requests with a body
// Kotlin Example using OkHttp for POST request
import okhttp3.OkHttpClient
import okhttp3.FormBody
import okhttp3.Request

fun main() {
    val client = OkHttpClient()

    val formBody = FormBody.Builder()
        .add("username", "USERNAME")
        .add("password", "PASSWORD")
        .add("account", "ACCOUNT_ID")
        .add("customer_id", "361")
        .add("invoice_lines[0][title]", "Consulting hours")
        .add("invoice_lines[0][quantity]", "2.5")
        .add("invoice_lines[0][unit_price]", "100")
        .add("invoice_lines[0][vat_percent]", "24")
        .add("invoice_lines[0][discount]", "10")
        .add("invoice_lines[0][pid]", "4475")
        .add("invoice_lines[0][line_note]", "Phase 1")
        .add("invoice_lines[0][product_code]", "CONS-1")
        .add("invoice_lines[1][title]", "Hosting service")
        .add("invoice_lines[1][quantity]", "1")
        .add("invoice_lines[1][unit_price]", "80.05")
        .add("invoice_lines[1][vat_percent]", "25.5")
        .add("due_date", "2026-10-20")
        .add("note", "Thank you for your business!")
        .add("send_methods[0]", "email")
        .add("send_methods[1]", "sms")
        .add("send_methods[2]", "einvoice")
        .build()

    val request = Request.Builder()
        .url("https://easycms.fi/public_api/set_invoice")
        .post(formBody)
        .addHeader("Authorization1", "TOKEN")
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("Unexpected code $response")

        println(response.body?.string())
    }
}

using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
    static async Task Main()
    {
        var token = "TOKEN";
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair("username", "USERNAME"),
            new KeyValuePair("password", "PASSWORD"),
            new KeyValuePair("account", "ACCOUNT_ID"),
            new KeyValuePair("customer_id", "361"),
            new KeyValuePair("invoice_lines[0][title]", "Consulting hours"),
            new KeyValuePair("invoice_lines[0][quantity]", "2.5"),
            new KeyValuePair("invoice_lines[0][unit_price]", "100"),
            new KeyValuePair("invoice_lines[0][vat_percent]", "24"),
            new KeyValuePair("invoice_lines[0][discount]", "10"),
            new KeyValuePair("invoice_lines[0][pid]", "4475"),
            new KeyValuePair("invoice_lines[0][line_note]", "Phase 1"),
            new KeyValuePair("invoice_lines[0][product_code]", "CONS-1"),
            new KeyValuePair("invoice_lines[1][title]", "Hosting service"),
            new KeyValuePair("invoice_lines[1][quantity]", "1"),
            new KeyValuePair("invoice_lines[1][unit_price]", "80.05"),
            new KeyValuePair("invoice_lines[1][vat_percent]", "25.5"),
            new KeyValuePair("due_date", "2026-10-20"),
            new KeyValuePair("note", "Thank you for your business!"),
            new KeyValuePair("send_methods[0]", "email"),
            new KeyValuePair("send_methods[1]", "sms"),
            new KeyValuePair("send_methods[2]", "einvoice")
        });
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Add("Authorization1", token);
            var response = await httpClient.PostAsync("https://easycms.fi/public_api/set_invoice", content);
            if (response.IsSuccessStatusCode)
            {
                var responseData = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseData);
            }
            else
            {
                Console.WriteLine($"Error: {response.StatusCode}");
            }
        }
    }
}

Handling Endpoint Results

When you make a request to the endpoint, you receive a JSON response containing various keys and values. Here's an explanation of the response keys and their meanings:


{
    "OUTPUT": {
        "response_type": "success",
        "message": "invoice_was_created_successfully",
        "invoice": {
            "invoice_id": 1042,
            "reference": "10753096392167",
            "title": "Invoice",
            "status": "open",
            "status_flags": [],
            "invoice_date": "2026-10-06 14:02:11",
            "due_date": "2026-10-20 00:00:00",
            "delivery_date": "2026-10-06 14:02:11",
            "currency": "EUR",
            "total": 380.63,
            "total_net": 305.05,
            "total_vat": 75.58,
            "total_paid": 0,
            "total_due": 380.63,
            "vat_rows": [
                { "vat_percent": 24, "net": 225.0, "vat_amount": 54.0, "gross": 279.0 },
                { "vat_percent": 25.5, "net": 80.05, "vat_amount": 20.41, "gross": 100.46 }
            ],
            "lines": [
                {
                    "invoice_line_id": 9011,
                    "number": 1,
                    "pid": 4475,
                    "title": "Consulting hours",
                    "product_code": "CONS-1",
                    "quantity": 2.5,
                    "unit": "pcs",
                    "unit_price": 100,
                    "unit_price_is_net_excl_vat": true,
                    "discount_percent": 10,
                    "vat_percent": 24,
                    "line_note": "Phase 1",
                    "line_total_gross": 279.0
                },
                {
                    "invoice_line_id": 9012,
                    "number": 2,
                    "pid": null,
                    "title": "Hosting service",
                    "product_code": null,
                    "quantity": 1,
                    "unit": "pcs",
                    "unit_price": 80.05,
                    "unit_price_is_net_excl_vat": true,
                    "discount_percent": 0,
                    "vat_percent": 25.5,
                    "line_note": null,
                    "line_total_gross": 100.46
                }
            ],
            "transactions": [],
            "sent_log": [
                { "date": "2026-10-06 14:02:12", "type": "email", "sender": "API-Shop_1" },
                { "date": "2026-10-06 14:02:14", "type": "sms", "sender": "API-Shop_1" }
            ],
            "customer": { "customer_id": 361, "firstname": "Maija", "lastname": "Meikäläinen", "email": "[email protected]", "billing_email": "[email protected]" },
            "note": "Thank you for your business!",
            "invoice_url": "https://yourdomain.fi/invoice.php?ref=8f14e45fceea167a5a36dedd4bea2543&id=343",
            "barcode": "4"
        },
        "send_results": {
            "email":   { "sent": 1, "error": null, "recipient": "[email protected]" },
            "sms":     { "sent": 1, "error": null, "recipient": "358401234567" },
            "einvoice": { "sent": 0, "error": "sender_business_id_required" }
        }
    },
    "INFO": { "start": 0, "limit": 50, "count": 1, "total_count": 1, "tip": "", "docs": "https://easycms.fi/DOCUMENTATION/bt5/set_invoice" }
}
    

Error Handling

Here are the possible error messages and their meanings:

  • customer_not_found_...: The customer_id does not exist in this account — register the customer first via the customer API calls.
  • invoice_lines_must_be_sent_as_a_non_empty_array_via_api: invoice_lines is missing or empty.
  • invoice_lines_0_title_is_required / invoice_lines_0_quantity_must_be_a_positive_number / invoice_lines_0_unit_price_must_be_a_non_negative_number: A line is missing its title, a positive quantity or a non-negative net unit price.
  • invoice_lines_0_vat_percent_is_not_valid_for_this_account_valid_values_are_...: The VAT percent does not exist in the account's VAT table; the message lists the valid values.
  • invoice_lines_0_pid_does_not_exist_in_this_account: The optional pid does not match any product in this account.
  • invoice_lines_0_discount_must_be_between_0_and_100: Line discount out of range.
  • maximum_100_invoice_lines_per_set_invoice_call: More than 100 lines in one call.
  • send_methods_contains_unknown_channel_...: send_methods contains anything other than email, sms or einvoice.
  • Per-channel send errors (returned in send_results, the invoice is still created): no_email_address, invalid_email_address, email_not_valid (email_check=...) (recipient refused by the email-existence gate), no_email_template_configured_for_this_account, no_phone_number, invalid_number, insufficient_balance, no_sms_template_configured_for_this_account, sms_send_failed_errno_..., sender_business_id_required, e_invoice_send_failed_....
  • UN-AUTHORIZED - ...: Authentication failed (wrong TOKEN/username/password/account).