API documentation for Iteras

    Server-side API

    To get information out of Iteras, you can call an Iteras server directly through HTTPS.

    The result is returned as JSON. In case of an error in the way the request is submitted (i.e. a programming or server authentication error), a response with a 40x HTTP status code is returned, e.g. 400 or 404.

    Common issues


    The authentication mechanism is currently a pre-shared key that you can find inside your account settings in Iteras.

    The preferred way to provide the key is by inserting an HTTP header X-Iteras-Key with the key as the value, e.g.

      X-Iteras-Key: 00aabbccddff112233445566778899

    If this proves difficult, you can also provide the key as the GET/query variable iteras-key, e.g.



    Some parts of the API returns text intended for an end user. The default language used is the language of the first business entity inside Iteras.

    If you need to override this, you can provide a "language" GET parameter, with "da" for Danish, "en" for English, etc. For instance: ?language=da

    Authenticate customer

    The /api/authenticatecustomer/ endpoint authenticates a given customer id/email + password combination.

    The endpoint supports POST only so you must post the parameters in the same manner as the browser, i.e. application/x-www-form-urlencoded.

    Since the Iteras server doesn't know the origin of the parameters provided, there is no intrinsic rate limit. Hence, if you connect an internet-facing application to this endpoint, you should put in a rate limit to prevent hacking by exhaustive search.

    The endpoint currently supports the following parameters - you must provide the password and either id or email:

    • "id" - customer/subscriber ID, e.g. id=12345

    • "email" - customer/subscriber email, e.g. email=somebody@example.com

    • "password" - customer/subscriber password, e.g. secret

    • "preauthseconds" - request a pre-authentication token valid for given seconds, e.g. 3600

    The data returned is an object with authenticated: true and the ID of the customer like this:

      "authenticated": true,
      "id": "12345"

    Or if authentication fails:

      "authenticated": false,
      "errorcode": "unknowncustomer"


      "authenticated": false,
      "errorcode": "invalidpassword"

    If you specify preauthseconds, you'll get back a pre-auth token in the reply in case the user is authenticated, e.g.:

      "authenticated": true,
      "id": "12345",
      "preauth": "Azxd123:JOIj123:JOjOIJASDF"

    This token is only valid for the number of seconds you specified. When the pre-auth token is given to the iframe-generating JS API it causes the customer to be logged in automatically without being presented with a login screen:

      Iteras.selfserviceiframe({ profile: "myprofile", preauth: "Azxd123:JOIj123:JOjOIJASDF" })

    This is sometimes useful for making a single-signon site.

    Customer data

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

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

    • "id" - a comma-separated list of customer/subscriber ids, e.g. ?id=12345,42312. If not specified you'll get information on all customers.

    • "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 information about periods, begin/end dates and products received in each period
    • "filter" - a set of conditions to filter the returned customers by, in JSON, e.g. ?filter=[{ "field": ":Custom field", "operator": "filledin" }]

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

      A single condition is currently on the form field on customer satisfies operator where "field" is the name of the field and "operator" is currently one of:

      • "filledin" or "notfilledin" - satisfied/not satisfied if the field has a value and that value is not an empty string or false

    The data returned is an object with an array of customer information (possibly empty array), like this:

      "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",
            ":Custom field": "Some value"
          "active_subscriptions": [
              "campaign_name": "3 issues",
              "campaign_id": "3i",
              "group_customer_id": "123456",     # filled in if it's a recipient subscription
          "subscriptions": [
              "data": {
                ":Custom subscription field": "Value here"
              "periods": [
                  "campaign_name": "3 issues",
                  "campaign_id": "3i",
                  "begin": "2010-12-24T12:00:00",
                  "end": null,
                  "group_customer_id": "123456",     # filled in if it's a recipient subscription
                  "products": [
                      "name": "Name",
                      "date": "2011-12-26",
                      "external_id": "External ID"

    Note that Iteras internally has more processing information about the customers. If you need more than what's currently available, please contact us.

    Update customers

    The /api/customers/update/ endpoint lets you update customer/subscriber data.

    The endpoint uses POST and currently supports the following parameters:

    • "updates" - a JSON array of objects where you provide the customer/subscriber ID as "id" and the "data" as on object with the fields to set on the customer

    The return value is a JSON object with errors, if any.

    An example of the JSON array to pass in "updates":

        "id": "12345",
        "data": {
          "name": "New Name",
          ":Custom field": "Some value",
          ":Custom date field": "2001-12-31",
          ":Obsolete field": null        # passing null deletes the field from the customer

    Fields not mentioned in "data" are not touched.

    Supported fields: Currently you can basically change fields that you can get from /api/customers/, with a few small inconsistencies due to backwards compatibility: use "country" instead of "country_code", and "address" instead of "location".

    Note that changing the data for a customer adds an entry to the history log inside Iteras summarizing the change. In case you're periodically synchronizing some kind of statistics where you don't want a history entry per update, you can disable history for the specific field you're updating in the field settings.


    Basic validation is done on the changed fields, and if a field is found to be invalid, the whole update for that customer is skipped and the validation errors returned instead. The following updates in the array are still processed.

    In case a customer ID is not found, the update for that customer is silently skipped.

    Validation errors are returned with an array of the same length as the "updates" array you have passed in, with an object (empty if no errors) for each update with fields and corresponding errors in human-readable form, like this:

      "errors": {
        "updates": [
            ":Custom date field": ["Enter a valid date."]

    Note that the errors returned in this manner are errors from user input and return with an HTTP 200 status code. Programming errors, e.g. badly formatted customer IDs or errors in the structure of the passed in JSON, are reported through HTTP error codes.

    Javascript API

    The Javascript API allows you to integrate Iteras functionality directly into another site. To get access to the API, include https://app.iteras.dk/static/api/iteras.js in your page, as in

      <script src="https://app.iteras.dk/static/api/iteras.js" type="text/javascript"></script>

    Then you can call various methods on a global Iteras object, e.g.

        document.write(Iteras.orderingiframe({ profile: "myprofile", orderingid: "XXXXX" }));


    There are a bunch of methods for generating iframes to be included on your site. Common parameters are:

    • profile which is a short lower-case descriptive string to use for the URL, e.g. if your business entity name is "Foo Bar" it will typically be "foobar"; you can find it in your settings inside Iteras.
    • cssclass which sets a CSS class on the body element inside the iframe, in case you need to customize a particular iframe.
    • preauth which given a pre-auth token causes a given customer to be logged in automatically.
    • next which overrides what page to redirect to after the action is done, e.g. after ordering

    Note that logging in to the self-service does not log you in to a paywall and vice versa. Paywall sessions are usually long, to reduce hassle for paying customers, whereas access to the self-service should usually be kept short as people only rarely need to manage their data.

    Subscriber self-service iframe

      Iteras.selfserviceiframe({ profile: "myprofile" })

    Generates an iframe with the subscriber self-service pages. The first page will be a login page.

    Ordering form iframe

      Iteras.orderingiframe({ profile: "myprofile", orderingid: "XXXXXX" })

    Generates an iframe with an ordering form. "orderingid" is the URL id for the ordering form inside Iteras (it’s usually either a randomly generated string or something descriptive that you have chosen).

    Note that you can provide the parameter next with a URL to redirect to after the order is completed.

    Paywall login iframe

      Iteras.paywalliframe({ profile: "myprofile", paywallid: "XXXXXX", next: "/" })

    Generates an iframe with login to the paywall. The paywall id can be found in the paywall settings. "next" is a path to redirect to upon succesful login in case there isn’t a more appropriate redirect known (the iframe looks for an iterasnext GET parameter in the page it is inserted.

    The paywall login page uses the iframe to set a cookie on your domain with paywall access info - see the paywall section for details. This cookie does not log the visitor into the self-service.

    Note that paywall login iframe is intended to redirect to another page. If you don't give it redirection information or just somehow redirect to the same page so it is rerendered, it will show the login form again. Don't get confused! If your browser has the cookie, you're logged in.

    You can set the required access level with "access" (see the paywall section for more info), e.g.

      Iteras.paywalliframe({ profile: "myprofile", paywallid: "XXXXXX", access: "user" })

    In case you need to login to multiple paywalls at the same time, e.g. if each paywall walls off a separate section, you can specify a list of paywall ids instead of a single id, e.g.:

      Iteras.paywalliframe({ profile: "myprofile", paywallid: ["XXXXXX", "YYYYYY"], next: "/" })

    IP-based authentication: IP-based access control works the way that the paywall iframe will automatically check the IP address and log in visitors with IP-based access without going through the usual password flow. When such a visitor is logged in, the rest of the flow then works as usual.

    Inserting an iframe

    The iframe methods generate a string with HTML that you can either insert directly in the page with document.write, e.g.

        document.write(Iteras.orderingiframe({ profile: "myprofile", orderingid: "XXXXX" }));

    or you could use a framework, e.g. with jQuery

      <div id="ordering-iframe-container"></div>
        $(function () {
          $("#ordering-iframe-container").append(Iteras.orderingiframe({ profile: "myprofile", orderingid: "XXXXX" }));

    The iframe will automatically resize its height based on the height of its content. Note that for this to work, the iteras.js script must be included on the page (as would normally happen when you use the API).

    If you want to control the height or width yourself, we recommend you put the iframe (or document.write <script>) inside an element you control yourself and set max-width/max-height on that with CSS. This prevents conflicts with the styles applied by the Iteras API.

    Common questions/problems

    The iframe has sizing problems/looks weird?

    It’s common to end up with a bunch of CSS files to style various things. There is probably something in one of those files with a rule that accidentally applies to the Iteras iframes. Rules to look out for are both rules on iframes and rules on the parent elements of the Iteras iframes, e.g. if the iframe doesn’t automatically increase its height, there could be a rule that restricts the max height of the elements where the iframe is embedded.

    To debug this, inspect the inserted iframe with the developer tools in your browser and see what rules are applied to the element and its parents.

    How do I control the style of the iframe itself, e.g. width/height?

    Don’t. Put it inside a wrapper element (e.g. a <div>) and apply your styles to that element. This prevents conflicts with the styles applied by the Iteras API.

    How do I control the looks of the iframe contents?

    Due to separate security contexts in the browser, the iframe content ignores outside rules. So you have to include your CSS through the website customization fields in the customer service settings inside Iteras. You can include the styles inline or through a link to a CSS file hosted on your own site.

    How do I apply different styles for different iframes?

    Use the "cssclass" parameter when generating the iframe, it sets a CSS class on the body element inside the iframe so you can the hook up on that.


    The paywall works the way that visitors are denied access unless they have a pass cookie called "iteraspass" that authorizes them. This cookie is set (on your domain) by the paywall login iframe upon successful login and is normally valid for an extended period of time.

    This design minimizes the coupling between your system and Iteras. Visitors do not depend on access to an Iteras server to view content besides the initial login, improving fault tolerance and response times.

    In order to protect against subscriptions running out or being stopped, the pass cookie should be regenerated from time to time by Iteras - if the check fails, the cookie is deleted so the user is denied access.

    To get started, you need to add a paywall inside Iteras and configure its settings there. This gives you a paywall id for use in the API.

    How to use the paywall

    To setup a basic paywall you need to setup a landing page for people who are not logged in. On this page, you’d normally include an explanation that paywall access is required, a strong call to action for people who have not signed up yet and either the paywall login iframe or a link to a page with it.

    Say the path to your landing page is "/paywall/landing.html", then you simply call

      Iteras.wall({ paywallid: "XXXXXX", redirect: "/paywall/landing.html" })

    on all pages you want paywalled. This call checks that a cookie with the right paywall id exists, if not it redirects to the URL given by "redirect". In case you need to check for a pass to either of a number of paywall ids, put in a list, like this

      Iteras.wall({ paywallid: ["XXXXXX", "YYYYYYY"], redirect: "/paywall/landing.html" })

    The call to Iteras.wall will also automatically regenerate the cookie from time to time by contacting the Iteras server.

    The landing page may get some query/GET parameters starting with "iteras", e.g.


    These are read by the paywall login and ordering iframes, so if you don’t have the iframes embedded directly in the landing page but instead accessible through links, you should append all query parameters starting with "iteras" to those links. Otherwise, visitors will have reduced functionality, e.g. people will not be redirected back to the original page upon logging in.

    Logging out of the paywall

    To log out from the paywall, call



      <button id="logout">Log out</button><
        document.getElementById("logout").onclick = function () {

    Upon completing the log out, by default the page will be reloaded. You can redirect to another page by specifying a redirect option, like this

      Iteras.logoutFromWall({ redirect: "/some/url" })

    You can also specify redirect: null to prevent the reload.

    Custom paywall handling

    In case you need something else than a redirect upon un-authorized access, you can provide an "unauthorized" callback to the Iteras.wall function instead of the "redirect" parameter, like this

      function myPaywallHandler() { ... }
      Iteras.wall({ paywallid: "XXXXXX", unauthorized: myPaywallHandler })

    Note that the wall function may call the handler or return immediately and call the handler later. The latter may for instance happen in case the pass cookie is regenerated (which may take some milliseconds) and it turns out the visitor no longer has access.

    If you omit both "redirect" and "unauthorized", nothing will happen in case of an unauthorized visitor, but the code will still check the pass cookie from time to time and delete it if it is no longer valid.

    Access levels

    There are currently two access levels in Iteras.

    • Visitors with active subscriptions (on campaigns with digital access) are at the "sub" level.
    • Visitors registered as subscribers but with no active subscriptions are at the "user" level.

    It is sometimes useful to give the user-level visitors access to selected areas so you can configure Iteras to hand out paywall passes to them, and then add the "access" parameter to the wall function, like this

      Iteras.wall({ paywallid: "XXXXXX", redirect: "/paywall/landing.html", access: "user" })

    for user-accessible pages and

      Iteras.wall({ paywallid: "XXXXXX", redirect: "/paywall/landing.html", access: "sub" })

    for pages for the actual subscribing subscribers.

    Paywall pass cookie

    The paywall pass cookie consists of a number of fields separated by | with a signature at the end beginning with /, e.g.


    The fields can’t contain | or other characters not allowed in cookies so a simple string split can be used for parsing. The fields are currently:

    • access level - in case of multiple paywall ids, the level for each paywall is separated with a comma in the same order as the paywall ids in the next field
    • paywall id - in case of multiple paywall ids, they are separated with a comma, e.g. XXXXX,YYYYY
    • UTC timestamp for when the pass expires (normally requiring new login)
    • subscriber id
    • IP address of other end when pass was granted

    You can count on the ordering of the fields, but we might add more fields at the end in the future so don’t write code that assumes that the number of fields is fixed.

    The signature is everything to the right of the last / in the pass and consists of a signing algorithm identifier followed by : followed by the actual signature. The signing algorithm is run on everything to the left of the /.

    "sha256" means HMAC SHA256 with hexadecimal numbers as output (this is a standard algorithm with implementations in many programming environments). The key used for the HMAC is the API key you can find in the settings inside Iteras.

    The signature is not checked in the JS API, but you can check it server-side to ensure that the cookie was actually generated by Iteras.

    Server-side check

    Note that the Javascript paywall API here as a client-side solution allows sufficiently skilled people to trick the paywall.

    If this is a problem, make a server-side check of the pass cookie as described in the previous section and modify the server to prevent content from being sent to people without a valid cookie.

    However, as long as it is difficult enough to circumvent the paywall for 99.999% of your target audience - especially on relatively locked-down consumer tablets and phones - it may not be cost-effective to take this extra step.