Tenant API - Content

Send content

Metadata is the data that Kivra needs to be able to deliver the Content to the right User. It may also determine how the User can interact with the Content.

Minimum Metadata

As a minimum one valid recipient identifier (vat_number, ssn or email), a subject and type are required.

Responsive content

As more than 90% of the content sent in Kivra is consumed on mobile devices, it is important to be able to show to the recipient the most important information in an easily readable way, without relying on the recipient to zoom in on the area of the PDF where the information is presented.

Responsive content solve this problem by allowing senders to provide 2 versions of the content:

  • a PDF which contains all the information and details that needs to be presented to the recipient
  • an HTML version of the same content that includes an overview of the information and highlights what the sender wants to present to the user.

Example on how to use HTML resp. PDF in your communication:

  • A letter where the PDF contains all the information that the letter must include, while the responsive part (HTML) contains only important dates, actions the user needs to take and the phone number to support.

The HTML part should be a simpler introduction that surfaces the most important information from the content. It must relate to the content itself — not a generic promotional banner.

Restrictions for the HTML part

Kivra is an archive and it is very important that content sent in Kivra is immutable and does not update itself over time. The recipient should be able to return to a received content at any point in time and see it exactly as it was when it was received.

As HTML is a format that offers many possibilities there are restrictions on how the HTML is formed:

  • No javascript: no javascript allowed, neither inline or fetched from external resources
  • External documents such as images, icons, css, etc. must be embedded inline in the HTML file to avoid external links to become bad over time and break the rendering of the HTML

Privacy is also an important concern for Kivra. Links containing UTMs are allowed in the HTML but cannot be used to track the behavior of each particular user.

Retained Content

A retained Content is a Content that is sent to a Recipient who is not yet a user of Kivra. Once that Recipient register with Kivra, the Content will be delivered to that Recipient’s Kivra account. Retained Content has a time limit for how long they can be retained before being deleted, ie removed unless the Recipient registers within a given period of time.

<aside class="notice"> Note: Usage of retained functionality is only allowed for documents of type `letter.creditnotice`, `letter.salary`, `salaryslip`, `letter.signed` and `invite.sign`, otherwise an error with status code `422` will be returned </aside>

Sending retained Content uses all the same attributes as a normal Content with the difference for some additional metadata-attributes. By enabling the retain metadata attribute and setting it to true will enable possible retention of a Content. Kivra’s logic is to first look if the Recipient exist. If it does Kivra will deliver the Content as usual, if the Recipient doesn’t exist Kivra will retain the Content for the specified amount of time (30 days is the default value if nothing else is specified). This makes it easy for an Integrator to issue a "retain or deliver"-logic for all it’s Contents.

Integrators that want to retain a Content for a different time-period than Kivra’s default can use the retention_time metadata.

Duplicate control

If exactly the same payload (content plus metadata, with only exception being the generated_at field) is received more than once, only the first occurrence will result in a delivery. In the other occurrences an OK message will be returned, but no corresponding content will be delivered to the receiver. This is a security mechanism to allow senders to safely re-send the same payload in case there is uncertainty on whether the previous sending resulted in a delivery or not. A typical example is in case of timeout error, where the sender cannot establish whether the sending resulted in a content delivery or not.

<aside class="notice"> Note: Duplicate control is only available in production, not in sandbox. This allows senders to reuse the same payload several times in the test environment. </aside>

It's important to underline that the duplicate control is made by checking the complete payload (beside generated_at), not only the attached content. This means that any change in the payload will cause the duplicate check to fail and the corresponding content to be delivered.

Another important consequence is that in case a sender wants to safely re-send a PDF content, it is important that the PDF is not generated again between the API calls as this almost certainly results in a slightly different PDF and therefore in a different payload, meaning that the duplication check will not be able to recognise it as a duplicate.

post/v2/tenant/{tenantKey}/content

Request

  • Base URL: https://sender.api.kivra.com
  • URL: https://sender.api.kivra.com/v2/tenant/{tenantKey}/content
  • Auth: oAuth2Client (defined in the document in a form this page cannot read)

Path parameters

tenantKeystring hexadecimal required

The unique Key for a Tenant

Request body

OR

Example request

{
  "ssn": "191212121212",
  "email": "Kivra.Sweden@example.com",
  "subject": "Invoice for March 2022",
  "generated_at": "2022-12-31",
  "type": "letter",
  "retention_time": "30",
  "parts": [
    {
      "name": "document.pdf",
      "data": "REVBREJFRUY=",
      "content_type": "application/pdf",
      "responsive_part": {
        "name": "document.html",
        "data": "PGgxPmNvbnRlbnQgMTwvaDE+CjxwPmNvbnRlbnQgMTwvcD4=",
        "content_type": "text/html"
      }
    }
  ],
  "payment_multiple_options": {
    "method": "1",
    "account": "1234-5678",
    "currency": "SEK",
    "options": [
      {
        "due_date": "2017-01-01",
        "amount": "123.50",
        "type": "SE_OCR",
        "reference": "426523791",
        "icon": {
          "name": "filename.pdf",
          "data": "REVBREJFRUY=",
          "content_type": "image/png"
        }
      }
    ],
    "flexible_option": {
      "due_date": "2017-01-01",
      "type": "SE_OCR",
      "reference": "426523791",
      "icon": {
        "name": "filename.pdf",
        "data": "REVBREJFRUY=",
        "content_type": "image/png"
      },
      "min_limit": "123.50",
      "min_suggested": "123.50",
      "max_suggested": "123.50",
      "max_limit": "123.50"
    }
  },
  "booking": {
    "title": "Kontroll av tandhälsa",
    "start_time": "2028-06-19T11:00:00Z",
    "end_time": "2028-06-19T12:00:00Z",
    "location": "Strandvägen 61, Stockholm",
    "description": "Brush your teeth before coming"
  },
  "campaign": {
    "tag": "summer_2023"
  },
  "form": {
    "id": "2877d684-a340-4e4c-867f-d93283787b01",
    "sender_reference": {
      "internal_id": "6eb7d1b9-ccd6-408f-ade7-9a1837be3ecf"
    },
    "days_to_expiry": 30
  },
  "invite": {
    "description": "This is the heavy heavy monster sound\nThe nuttiest sound around\n",
    "uri": "https://example.com",
    "behalf_of": {
      "name": "Test Inc",
      "vat_number": "SE556840226601"
    },
    "expires_at": "2024-05-09T13:42:26Z"
  },
  "salaryslip": {
    "document_metadata": {
      "creation_timestamp": "2025-01-05T10:00:00Z",
      "source_system": "Payroll System XYZ"
    },
    "employment": {
      "start_date": "2025-01-01",
      "type": "full-time",
      "weekly_hours": "40.0",
      "organizational_unit_identifier": "DEPT-001",
      "employer": {
        "legal_name": "Tech Solutions AB",
        "legal_identifiers": {
          "vat_number": "SE212000014201",
          "org_number": "212000-0142"
        },
        "contact_information": {
          "address": {
            "street_address": "Storvägen 5",
            "postal_code": "164 40",
            "city": "Kista",
            "country": "Sweden"
          },
          "email_address": "hr@techsolutions.se",
          "telephone_number": "+46 770-457 000"
        }
      }
    },
    "yearly_accumulated_gross_pay": {
      "monetary_amount": "140000.00",
      "currency": "SEK"
    },
    "yearly_accumulated_taxes": {
      "monetary_amount": "30000.00",
      "currency": "SEK"
    },
    "pay_period": {
      "start_date": "2025-01-01",
      "end_date": "2025-01-31",
      "payout_date": "2025-02-25",
      "total_hours_worked": "160.0",
      "overtime_hours": "8.0",
      "effective_tax_rate_percentage": "21.43",
      "absences": [
        {
          "type": "sick leave",
          "duration_in_hours": "8.0"
        }
      ],
      "vacation_pay": {
        "monetary_amount": "1500.00",
        "currency": "SEK"
      },
      "basic_contractual_salary": {
        "monetary_amount": "30000.00",
        "currency": "SEK"
      },
      "gross_pay": {
        "monetary_amount": "32500.00",
        "currency": "SEK"
      },
      "tax_deducted": {
        "monetary_amount": "6962.50",
        "currency": "SEK"
      },
      "additional_payments": [
        {
          "type": "overtime",
          "monetary_amount": "2000.00",
          "currency": "SEK"
        }
      ],
      "net_pay": {
        "monetary_amount": "25537.50",
        "currency": "SEK"
      },
      "employee_benefits": [
        {
          "type": "gym membership",
          "monetary_amount": "1500.00",
          "currency": "SEK",
          "description": "Monthly gym allowance."
        }
      ]
    },
    "tax_information": {
      "tax_code": {
        "identifier": "Tabell 30",
        "schema": "Skatteverket Tax Table"
      },
      "tax_deduction_details": {
        "is_main_income": true,
        "details_provided_by": "Skatteverket"
      }
    },
    "recipient_account": {
      "identifier": "SE4550000000050012345678",
      "schema": "IBAN",
      "bank_name": "Bank of Sweden"
    },
    "holidays": {
      "period": {
        "start_date": "2024-04-01",
        "end_date": "2025-03-31"
      },
      "total_days_entitled": "25.0",
      "days_saved_from_previous_years": "3.0",
      "days_used_in_period": "0.0",
      "days_remaining_in_period": "28.0"
    }
  }
}

Response

Content Created successfully

subjectstring

Same data as provided by the sender in the input

generated_atstring ISO8601

Same data as provided by the sender in the input

typestring

Same data as provided by the sender in the input

Example response

{
  "subject": "Invoice for November 2025",
  "generated_at": "2025-10-31",
  "type": "invoice"
}

Changes

No recorded changes to this operation across all 1 revision of this API.