# Retrieving customer data

The /api/customers/ endpoint enables you to retrieve customer data.

The endpoint allows you to get data for all your customers, which is sometimes very useful. However, if you have a lot of customers and need to get their data synchronized to another system many times a day, you cannot use this API endpoint. Instead check the section on synchronizing data.

# Parameters

The endpoint uses GET and currently supports the following parameters, all optional:

  • "id" - a comma-separated list of customer IDs, e.g. ?id=12345,42312. If left out, information on all customers is returned.

  • "fields" - a comma-separated list of fields to return in the JSON, e.g. ?fields=data,subscriptions. The default is "data,active_subscriptions". You can choose between:

    • "data" - all fields directly on the customer, such as name and email address.
    • "active_subscriptions" - summary of currently active subscriptions
    • "subscriptions" - all subscriptions with data on each subscription and also information about periods, begin/end dates and products received
      • "subscriptions.current_period" - include current_period which contains the currently active period, the value is null if there is no active period, e.g. a stopped subscription or a subscription starting in the future
      • "subscriptions.begin" - include begin timestamp for each subscription
      • "subscriptions.cancelled" - include cancelled boolean on each subscription, cancelled is true if a request has been made to stop the subscription
      • "subscriptions.stop_requested" - include stop_requested timestamp on each subscription with the time a request was made to stop the subscription, if any
      • "subscriptions.price" - include the price on each subscription, if set
      • "subscriptions.payment_agreement" - include a payment_agreement on each subscription, if set
    • "invoices" - invoices for the customer
      • "invoices.download_url" - include a download_url on each invoice
      • "invoices.payment_url" - include a payment_url on invoices that can be paid
      • "invoices.lines" - include lines on each invoice with information on what is invoiced
    • "estimated_invoicings" - simulate future processing of the subscriptions for the customer and estimate the invoice lines that would result in
    • "history" - log of events related to the customer
    • "password" - used in combination with "data" field, this will add the hashed customer password to the data returned
    • "password_url" - used in combination with "data" field, for customers without a password this will add a URL where the customer can set their password
    • "preauth_token" - used in combination with "data" field, this adds a pre-auth token to each customer making it possible to automatically log them into embedded Iteras components
  • "max_results" - how many customers to retrieve at most, e.g. max_results=1000. If there are more results available, a next_url is returned which you can use to grab the next page. "max_results" is currently clipped to be between 1-10000. If left out, 10000 will be assumed.

  • "iframe" - set if returned URLs are intended to go in an iframe, e.g. iframe=1

  • "preauthseconds" - if set, URLs that go to the self-service get a pre-auth token valid for this many seconds so that the customer does not have to login to view that page, e.g. preauthseconds=3600 makes the URLs valid for an hour

  • "estimated_invoicings_until" - extends the simulation in estimated_invoicings until this date, e.g. ?estimated_invoicings_until=2024-09-20 (max is currently 2 years in the future) - you can also specify a relative date with +3m for "3 months from now"

  • "filter" - a set of conditions to filter the returned customers by, in JSON, e.g. ?filter=[{ "condition_type": "customer:field", "field": ":Custom field", "operator": "filledin" }]

  • "extrafield" - can be repeated to get additional computed fields, e.g. ?extrafield=delivered_to:formatted&extrafield=delivered_to:address:postcode

Some parameters, e.g. filter and extrafield, contain special characters - remember to URL encode them.

# Return value

The data returned is an object with an array of customer information (possibly empty array).

Note that the content of the "data" fields depends on what fields have been set up inside Iteras and filled in on a given customer and subscription. In general, only filled-in fields are returned, so for instance if a customer doesn't have a particular field set, it won't show up in that customer's data.

Try calling the API on a known customer and see what you get back. Here's an example with a few extra fields:

GET /api/customers/?id=12345,12346&fields=data,active_subscriptions
{
  "customers": [
    {
      "id": "12345",

      "data": {
        "name": "Test Person",
        "email": "someone@example.com",
        "formatted_address": "Test Person\nRådhuspladsen 2, 3. th.\n1550 København V\nDenmark",
        "location": "Rådhuspladsen 2, 3. th.\n1550 København V",       // address without name or country
        "country_name": "Denmark",
        "country_code": "DK",
        "created": "2024-03-20T11:30:59.278511",
        ":Custom field": "Some value"
      },

      "active_subscriptions": [
        {
          "campaign_name": "3 issues",
          "campaign_customer_facing_name": "Beautiful 3 issues",
          "campaign_id": "3i",
          "group": "recipient",              // "recipient", "admin" or "admin+recipient"
          "group_customer_id": "123456",     // filled in if it's a recipient subscription
          "group_subscription_id": "654321"  // points to the admin subscription
        }
      ]

    },

    ...
  ]
}

Note that Iteras internally has more processing information about the customers and subscriptions - subscription management can be relatively complex. If you need more than what's currently available, please contact us.

# Subscriptions

Here's an example where subscriptions are requested:

GET /api/customers/?id=12345,12346&fields=subscriptions,subscriptions.price,subscriptions.payment_agreement
{
  "customers": [
    {
      "id": "12345",

       "subscriptions": [
        {
          "id": "32134123",
          "state": "active"                  // or suspended/ended/stopped
          "data": {
            ":Custom subscription field": "Value here"
          },
          "price": 99.95,                    // included if "subscriptions.price" specified
          "currency": "EUR",                 // included if "subscriptions.price" specified
          "products": [                      // only filled in if there are individual products assigned to the subscription itself
            {
              "id": "9976483",               // id of product assignment
              "name": "Name",
              "external_id": "External ID",
              "data": {
                  ":Custom product field": "Product value",
              }
            }
          ],
          "group": "admin",                  // "recipient", "admin" or "admin+recipient"
          "periods": [
            {
              "campaign_name": "3 issues",
              "campaign_customer_facing_name": "Beautiful 3 issues",
              "campaign_id": "3i",
              "begin": "2024-04-20",
              "end": null,
              "current": true,               // if the period is currently active
              "subscribed_to": [
                {
                  "name": "Subscription Product Name",
                  "external_id": "External ID"
                }
              ],
              "products": [                  // products assigned to the period, e.g. issues contained in the subscription
                {
                  "id": "19976483",          // id of product assignment
                  "name": "Name",
                  "date": "2024-05-20",
                  "external_id": "External ID"
                }
              ]
            }
          ],
          "payment_agreement": {             // included if "subscriptions.payment_agreement" specified
            "text": "Mastercard: XX...XX2451 (06/2026)"
          }
        }
      ]
    },

    ...
  ]
}

# Invoices

Here's an example where invoices are requested:

GET /api/customers/?id=12345,12346&fields=invoices,invoices.download_url,invoices.payment_url,invoices.lines&iframe=1&preauthseconds=3600
 {
   "customers": [
   {
     "id": "12345",

     ...

     "invoices": [
       {
         "invoice_number": "1001",
         "invoice_type": "invoice",
         "invoice_date": "2024-03-20",
         "due": "2024-04-03",
         "to_pay": 120.95,                   // amount not yet paid (0 if it has been paid)
         "download_url": "https://app.iteras.dk/example/iframe/invoice/1001/pdf/?...",
         "payment_url": "https://app.iteras.dk/example/iframe/ordering/summary/?invoice=...",
         "lines": [
           {
             "text": "Subscription 1 year",
             "amount": 120.95,
             "tax_rate": 0.25,
             "quantity": 2,
             "period_campaign_id": "1y",
             "period_begin": "2024-03-20T11:30:59.285988",
             "period_end": null,
           }
         ]
       },
       {
         "invoice_number": "1005",
         "invoice_type": "reminder",
         "invoice_date": "2024-04-10",
         "reminder": 1,
         "due": "2024-04-24",
         "to_pay": 170.95,
         "to_be_paid": true,                 // whether to show this as an invoice to pay, see below
         "download_url": "https://app.iteras.dk/example/iframe/invoice/1004/pdf/?...",
         "payment_url": "https://app.iteras.dk/example/iframe/ordering/summary/?invoice=...",
         "lines": [
           {
             "text": "Reminder fee",
             "amount": 50.0,
             "tax_rate": 0,
           }
         ]
       }
     ]

   },

   ...
 ]
}

The to_be_paid field on an invoice is set if the invoice should be presented to be paid. Now, you might think that you can simply inspect the to_pay amount, but in case the customer didn't pay an invoice and is sent a reminder, you may want to show that reminder instead of the invoice (it could have a reminder fee to be paid). To make this easier to do, to_be_paid is set for the most recent due invoices/reminders.

If you request a download URL with invoices.download_url, you can use preauthseconds to get a token appended to the download links to make them work without logging in for as long as specified.

# Estimated invoicings

You can ask the system to run a simulation of how it would invoice in the future for the customer and get back an estimate of that as an approximate timestamp and the associated invoice lines. You'll usually want to set estimated_invoicings_until your time horizon (e.g. 3 months). Here's an example:

GET /api/customers/?id=12345,12346&fields=estimated_invoicings&estimated_invoicings_until=2024-09-20
{
  "customers": [
    {
      "id": "12345",
      "estimated_invoicings": [
        {
          "at": "2024-08-03T11:30:59.287526",
          "lines": [
            {
              "text": "Subscription 1 year",
              "amount": 120.95,
              "tax_rate": 0.25,
              "subscription_id": "4321",
              "period_campaign_id": "1y"
            }
          ]
        }
      ]
    },

    ...
  ]
}

The estimated_invoicings is left out if the estimation does not find any future invoicings for the customer.

# History

Here's an example where history is requested:

GET /api/customers/?id=12345,12346&fields=history
{
  "customers": [
    {
      "id": "12345",

      ...

      "history": [
        {
          "text": "Something else happened",
          "timestamp": "2024-02-20T11:30:59.288270",
          "by": "System"
        },
        {
          "text": "Something happened",
          "timestamp": "2024-03-20T11:30:59.288308",
          "by": "somebody@example.com"
        }
      ]

    },

    ...
  ]
}

# Extra fields

Some fields have extra formatting options you can use. You request them with an extrafield parameter. The format is field_name:formattingoption. In case you want to avoid having a field with a ":" in it, you can name the resulting field with "=" like field_name:formattingoption=formatted_field_name. You can specify extrafield multiple times to add more fields.

Here's an example:

GET /api/customers/?id=12345&extrafield=delivered_to%3Aformatted&extrafield=delivered_to%3Apostcode%3Dpostcode
{
  "customers": [
    {
      "id": "12345",
      "data": {
         "delivered_to:formatted": "John Doe\nSome Road 123\n1000 København K\nDanmark",
         "postcode": "1000",
         ...
      }
    }
  ]
}

Currently address fields support the following formatting options:

  • "formatted" - multi-line textual representation of name, company, address and country
  • "postcode" - use the address parser in Iteras to try to extract the postcode
  • "address:split" - use the address parser in Iteras to try to split the address itself into components

# Pagination

To avoid having to handle a too large JSON document both in the sending and receiving end, the results are divided into pages. You can set the page size with max_results. If a query finds more results than what fits into the first page, the returned result contains a next_url where you can retrieve the next page, which may in turn have a next_url. Here's an example with 103 customers and a page size of 100:

GET /api/customers/?max_results=100
{
  "customers": [customer1, customer2, ..., customer100],
  "next_url": "https://app.iteras.dk/api/customers/?max_results=100&from=101"
}

Then when you query next_url, you get the remaining customers - note that since this is the end of the results, there is no next_url in the last page:

GET /api/customers/?max_results=100&from=101
{
  "customers": [customer101, customer102, customer103]
}

If you write code that may return a lot of customers, you should set max_results to a low value while you test your code to make sure you can handle the pagination correctly.

# Filtering

The filter parameter can either be a single condition or an array of conditions.

Each condition has a type condition_type which specifies what will be filtered on:

  • "customer:field": Customer must have a custom field matching the condition, e.g. ?filter=[{ "condition_type": "customer:field", "field": ":Custom field", "operator": "filledin" }].

    The field id is provided as field, and operator is one of:

    • "filledin" or "notfilledin" - satisfied/not satisfied if the field has a value (not an empty string or false)
    • "equal" or "notequal" - satisfied/not satisfied if the field has the value specified by value, e.g. ?filter=[{ "condition_type": "customer:field", "field": "email", "operator": "equal", "value": "customer@example.com" }]
  • "subscription:field": Customer must have a subscription with a field matching the condition. The field condition work as in the same manner as "customer:field".

  • "subscription:state": Customer must have a subscription in the given state, e.g. ?filter=[{ "condition_type": "subscription:state", "state": "active" }].

    Currently, state can be "active".

Note that a subscription filter besides filtering which customers are returned also filters the subscriptions returned on those customers.

# Synchronizing data

In order to synchronize data to another system continuously, you need to setup the Iteras webhook infrastructure. This way you will be notified when a customer has changed and get sent their new data.

Once you have a webhook setup and tested, you can use this API endpoint to get all the data to get in sync, and then simply rely on the webhook to tell you what's changed. In this way, the latency between a change happening and it being available in your end is minimal, with little load.

The webhooks are fault-tolerant so if something bad happens you will just get the changes resent. If something went wrong in your end, you can also rewind the webhooks a bit, or do a full resync through this API endpoint.

Just be aware that unless you have very few customers, or only resync infrequently, you do need to use the webhooks. We do not support using this API endpoint for frequent large synchronizations.