# Invoicing integration

Iteras supports integrating a new delivery method for invoices in addition to the built-in methods.

To use it, you need to go to the integration section and add and configure an invoicing integration. When you configure it, you set up an ID for the delivery method which is used by this API.

The new delivery method should show up inside Iteras. So you can make a test subscription, choose the new delivery method as the invoicing method, create the invoice on the page of the subscription, and you should be all set.

# The delivery flow

First the machinery inside Iteras finds subscriptions with uninvoiced periods that are ready for invoicing. This happens periodically and takes care of any timing policies set up inside Iteras. The found periods are then invoiced, if necessary on combined invoices, and then a delivery is created for each invoice to track further processing.

This is where your integration enters the picture:

  1. You grab a batch of waiting deliveries, automatically preparing them and the invoices in the process. The preparations happen just before sending to prevent stale data.
  2. Then you either send the whole batch at once if you happen to have a batch API you are sending through, or you send each delivery individually.
  3. Finally, you report back to Iteras whether the sending went well or not for each delivery. If it didn't go well, the delivery is either retried later, or invoicing fallbacks to another method.

# Preparing deliveries

POST to the /api/prepareinvoicedeliveries/<ID>/ endpoint to check if there are waiting deliveries for the delivery method ID. If so a batch of deliveries is prepared and returned. The following POST parameters are supported:

  • "max_results" (optional) - How many deliveries to return, e.g. 10. Max is 100. Don't take more than you can process in say 10 seconds to avoid concurrency issues. So if you spend 1 second per delivery, set max_results=10

  • "reschedule_seconds" (optional) - Number of seconds, e.g. 36000, to reschedule prepared deliveries to prevent them from turning up again immediately in case nothing is reported.

  • "fields" (optional) - Extra fields to include, lines and payment_url are the same as invoices.lines and invoices.payment_url for the customer data endpoint.

The endpoint returns a list of objects with some information for each delivery and an extra attribute more_deliveries_available: true if there are more deliveries ready to be sent besides the batch just returned.

The prepared deliveries are not removed from the queue of ready deliveries, unless reschedule_seconds is set, so if you call the endpoint again, you get the same deliveries back again, until you report a delivery status.

Note that it is not safe to call this endpoint concurrently from multiple processes. You may process the same deliveries twice.

Here's an example:

POST /api/prepareinvoicedeliveries/pigeons/ HTTP/1.1

max_results=10
fields=lines
fields=payment_url
{
  "deliveries": [
    {
      "delivery_id": "1234",
      "customer_id": "4321",
      "invoice_number": "1032",
      "invoice_type": "invoice",
      "invoice_date": "2024-10-08",
      "customer_billing_address": {
        "name": "Test Subscriber",
        "address": "Rådhuspladsen 2, 3. th.\n1550 København V",
        "country": "DK"
      },
      "to_emails": [
        "someone@example.com"
      ],
      "due": "2024-10-22",
      "to_pay": 249.95,
      "lines": [
        {
          "text": "1 Year of Value",
          "amount": 249.95,
          "tax_rate": 0.25,
          "period_campaign_id": "12M",
          "period_begin": "2024-10-08T19:40:11.866137",
          "period_end": "2025-10-08T19:40:11.866163"
        }
      ],
      "payment_url": "https://app.iteras.dk/..."
    }
  ],
  "more_deliveries_available": false
}

The data is basically in the format of the invoices section of the customer data endpoint with some extra fields.

# Reporting back delivery results

To report back what happened to a delivery, POST a list of delivery statuses to the /api/updateinvoicedeliveries/<ID>/ endpoint in the "deliveries" POST parameter as JSON, e.g. deliveries=[{"id": "1234"}, {"id": "1235", "error": true, "text": "Error sending ..." }, {"id": "1235", "error": true, "text": "Server example.com is down ...", retry_seconds: [86400, 172800, 604800]}].

Each delivery status object can have the following attributes:

  • "id" - The delivery_id returned in the prepared deliveries.

  • "text" (optional) - Allows you to add a description in case something special happened. This is useful for errors, but also in case you have some interesting info to add, e.g. if you deliver an email message, you might want to include the response from the other end, as in "Message accepted by example.com". Don't fill in the text field if you only have generic information, e.g. don't write "Delivered to Shipping Company" if you always deliver everything to Shipping Company anyway.

  • "error" (optional) - Set to true if the delivery failed. If the failure was perhaps temporary, fill in the retry_seconds list.

  • "retry_seconds" (optional) - In case of a temporary failure this is a list of seconds to approximately wait, e.g. [86400, 172800, 604800] would wait approximately 1 day after which the delivery becomes ready again, then if that sending fails, 2 days, and finally 7 days, for a total of approximately 10 days. If the last delivery fails too, the failure automatically becomes permanent. Iteras will keep track of the failures, so send the same array on each attempt.

Reporting delivery statuses removes the deliveries from the queue of ready deliveries. Preparing the deliveries does not do that in itself. Hence, if you call the prepare endpoint twice in a row without reporting back whether sending was succesful or not, you'll get the same deliveries in both calls.

This behaviour ensures that deliveries are not lost if your service crashes after having prepared the deliveries, but can be a problem if your delivery flow is asynchronous so you don't learn until later whether a delivery was succesful or not.

For asynchronous flows you could report back immediately anyway, i.e. reporting that the delivery is on its way. If you later get an failure, you can then update the status again.

Alternatively, you can set reschedule_seconds in the prepare endpoint to a sufficiently high value, say 10 hours later. This will move the prepared deliveries out of their ready state and reschedule them reschedule_seconds from now. So if everything goes awry, the deliveries will show up again in the ready queue 10 hours later. This can help prevent lost deliveries, but it can also, if you're not careful, result in duplicate deliveries.

# Robust delivery

The above flow has been designed to prevent and automatically handle common error scenarios. Some things to keep in mind:

  • You must report back that you handled a delivery or failed to handle it, otherwise the queue will never make progress.

  • Be aware of concurrency issues. If you have multiple processes preparing deliveries at the same time, they could end up getting the same deliveries twice. Prevent this from happening.

  • If everything fails for your delivery method, Iteras has often other options. The same is true for deliveries lost in transit. Iteras can send reminders automatically.