API documentation for Iteras

    Server-side API

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

    Getting started

    To be able to do anything, you need a Iteras account with an API key. From there on, it's a standard HTTP protocol.

    GET is used for requests that simply return data. Parameters are encoded as query parameters, i.e. appended to the URL as ?parameter1=value1.

    Endpoints using POST are supposed to be posted to just like a browser posts a form, e.g. with the parameters application/x-www-form-urlencoded.

    Use a recent HTTP library to make the requests, and this should be easy. In particular, you need to remember to encode the query parameters when supplying JSON - a library will do this for you automatically.

    Authentication

    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.

      https://app.iteras.dk/api/customers/?id=12345&iteras-key=00aabbccddff112233445566778899

    This also works for the POST endpoints.

    Results and error codes

    Results are returned as JSON.

    Generally, errors are handled in two separate ways for convenience. Errors from user input are returned in the JSON as documented in each call with an HTTP 200 status code. Programming errors in the way the request is submitted or a server authentication errors results in a plain text error response with a 40x HTTP status code, e.g. 400, 403 or 404.

    Customers, subscriptions, campaigns and products

    Inside Iteras, there are customers and products and campaigns. Each customer can have one or more subscriptions to one or more products. Each subscription progresses in consecutive periods, with each period having information about what is to be billed in the period and what is received. This is controlled through a campaign with each period having a separate link to the campaign for that period. When a period runs out, the campaign it is linked to determines if the subscription is renewed into a new period.

    If the product subscribed to is one that contains concrete products, e.g. issues of a magazine through a year, these concrete products are automatically assigned to the active period as the subscription progresses.

    It's also possible to assign individual concrete products directly to the subscriber, e.g. when selling a book or an event, or directly to the subscription (and not its periods) to allow customizing the subscription content beyond what can be setup with a campaign.

    Schemas

    Note that Iteras has support for changing what fields are available on customers, subscriptions and product assignments.

    So you generally need to try to get data from the server to know exactly what you get.

    Returned IDs

    Customer IDs and invoice and credit note numbers are stable.

    But you should probably not rely on other IDs to remain stable, i.e. if you get back the subscription IDs in one call, it's fine to forward it to another call, but you should not store it for a year and then count on being able to use it again.

    Language

    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" query parameter, with "da" for Danish, "en" for English, "sv" for Swedish, etc., for instance: ?language=da. This also works for the POST endpoints.

    Customer data

    The /api/customers/ endpoint enables you to retrieve customer 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
      • "history" - log of events related to the subscriber
    • "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

    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:

    {
      "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": [
            {
              "id": "32134123",
              "state": "active"                  # or suspended/ended/stopped
              "data": {
                ":Custom subscription field": "Value here"
              },
              "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",
                  }
                }
              ],
              "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": [                  # products assigned to the period, e.g. issues contained in the subscription
                    {
                      "id": "19976483",          # id of product assignment
                      "name": "Name",
                      "date": "2011-12-26",
                      "external_id": "External ID"
                    }
                  ]
                }
              ]
            }
          ]
    
          "history": [
            {
              "text": "Something else happened",
              "timestamp": "2010-12-24T12:00:00",
              "by": "System"
            },
            {
              "text": "Something happened",
              "timestamp": "2010-12-23T12:00:00",
              "by": "somebody@example.com"
            }
          ]
    
        },
    
        ...
      ]
    }

    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.

    Update customers

    The /api/customers/update/ endpoint processes an array of operations to be performed on a customer and related subscriptions and product assignments.

    Parameters

    The endpoint uses POST and currently supports the following single parameter:

    • "operations" - a JSON array of operation objects with customer ID as "id", with the name of an operation to perform in "operation" and then the parameters, see below

    Data

    Some of the operations have in common that you can change the fields of the object through a "data" object with some built-in attributes and then any fields set up inside Iteras.

    Fields not mentioned in "data" are not touched. Pass a field with a value of null to remove that field from the customer.

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

    Operation "updatecustomer"

    Changes the attributes of the customer. Parameters:

    • "data" - object with the attributes, valid attributes are a few built-in ones and then the customer fields defined inside Iteras

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "updatecustomer",
        "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
        }
      }
    ]

    Supported fields: Currently you can 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".

    Operation "updatesubscription"

    Update the data on a subscription. Parameters:

    • "subscription_id" - ID of the subscription to assign the product to

    • "data" - any fields to set on the subscription - the valid fields are the ones associated with the products that the subscription is subscribed to

    • "reinvoice" - this can be set to true if the update changes the pricing of the subscription to make the change take effect immediately, the system will then credit the future part of the subscription (if it has been invoiced already) and set it up to be invoiced

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "updatesubscription",
        "subscription_id": "32142532",
        "data": {
          ":Custom subscription field": "Some value",
          ":Obsolete field": null        # passing null deletes the field
        }
      }
    ]

    Operation "switchsubscriptionplan"

    Switch a subscription over to a new campaign. Parameters:

    • "subscription_id" - ID of the subscription to switch plan for

    • "new_campaign_id" - ID of the campaign to switch to

    The switch is done immediately. The ongoing period is stopped and a successor is created with the new campaign.

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "switchsubscriptionplan",
        "subscription_id": "32142532",
        "new_campaign_id": "3months"
      }
    ]

    Operation "cancelsubscription"

    Stop a subscription. Parameters:

    • "subscription_id" - ID of the subscription to switch plan for

    The stop is performed immediately, crediting the remainder of what it is owed for the current period, if any.

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "cancelsubscription",
        "subscription_id": "32142532"
      }
    ]

    Operation "assignproduct"

    Adds a product to a subscription. Parameters:

    • "subscription_id" - ID of the subscription to assign the product to

    • "name" or "external_id" - either name or the external ID of the product to assign

    • "data" (optional) - any attributes to attach to the product assignment, the valid attributes are the fields that are associated with the product

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "assignproduct",
        "subscription_id": "32142532",
        "name": "Test product",
        "data": {
          ":Custom product field": "Some value",
          ":Custom date field": "2001-12-31",
          ":Obsolete field": null        # passing null deletes the field
        }
      }
    ]

    Operation "updateproductassignment"

    Update the data on a product assignment. Parameters:

    • "product_assignment_id" - ID of the assignment, as returned by the /api/customers/ endpoint

    • "data" - any attributes to set on the product assignment - the valid attributes are the fields that are associated with the product

    • "reinvoice" - for a product assignment that is associated with a subscription (and not a period) this can be set to true if the update changes the pricing of the subscription to make the change take effect immediately, the system will then credit the future part of the subscription (if it has been invoiced already) and set it up to be invoiced

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "updateproductassignment",
        "product_assignment_id": "9989892798",
        "data": {
          ":Custom product field": "Another value",
          ":Obsolete field": null        # passing null deletes the field
        }
      }
    ]

    Operation "cancelproductassignment"

    Cancel a previous assignment of a product to a subscription. Parameters:

    • "product_assignment_id" - ID of the assignment, as returned by the /api/customers/ endpoint

    An example:

    [
      {
        "id": "12345",                   # customer ID
        "operation": "cancelproductassignment",
        "product_assignment_id": "9989892798",
      }
    ]

    Operation errors

    Basic validation is done on all operations. If something is found to be invalid, e.g. the value of a field, the whole operation is skipped and the validation errors returned instead. The next operations in the array are still processed.

    Return value

    The return value is a JSON object with a summary of succeeded and failed operations and the errors, if any.

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

    {
      "succeeded": 0,
      "failed": 1,
      "errors": [
        {
          ":Custom date field": ["Enter a valid date."]
        }
      ]
    }

    Errors that are not specific to a field are returned with the empty string as key "". For example:

    {
      "succeeded": 0,
      "failed": 1,
      "errors": [
        {
          "": ["Customer does not exist."]
        }
      ]
    }

    These errors 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 40x error codes.

    Place order

    The place order endpoint /api/placeorder/<ordering-url-id>/ puts in an order to an ordering form defined inside Iteras.

    This reuses the ordering form logic. For instance, you can inside Iteras decide to a make a field mandatory, or decide whether manual approval of new orders is required or not. If you unfamiliar with this, try setting up a form and place an order manually.

    Of course, some things won't work: Payment gateway interactions won't work as that depends on the gateway UI being available. Same for dynamic email duplicate checking/logging in - but you can pass in a customer ID.

    Note: You can't use a form with multiple product choices as the API doesn't allow passing in which product to choose at the moment.

    Parameters

    The endpoint uses POST.

    What POST parameters are supported depends on the ordering form setup - it's the same as those found in the form itself. If you're in doubt, you can try placing an order manually and check the POST'ed parameters in the browser development tool. Note that a few of those are optional and only used by the GUI, e.g. "accept_terms" and "products".

    For a simple name + email ordering form with a custom field named "Receive newsletter", you might send the following request:

      POST /api/placeorder/a72fb21d/
      name=John Doe
      email=johndoe@example.com
      :Receive newsletter=on
    

    Place an order for a specific customer by adding a customer parameter with the customer ID:

      POST /api/placeorder/a72fb21d/
      customer=123321
      name=John Doe
      email=johndoe@example.com
      :Receive newsletter=on
    

    Note that even if the customer already exists, you still have to pass in all the fields expected for the form, in the same manner as the UI. They're not filled in from the existing values on the customer. If you want them to be auto-filled, setup the form to not use those fields.

    The parameter can also be used to set the customer ID for new customers, in case you are creating customers that already have an ID in another system. The ID must be numeric.

    Return value

    The data returned is an object with placed: true. In case the ordering form is setup to automatically approve the order, the result also includes the customer ID of the resulting customer and the IDs of any subscriptions created:

    {
        "placed": true,
        "customer_id": "321415",
        "subscription_ids": ["12314232"]
    }

    Or in case of a validation error, an error object with the field and an array of error messages:

    {
        "placed": false,
        "errors": {
            "name": ["This field is required."],
            "email": ["Please enter a valid email address."]
        }
    }

    Use ?language=<code> to change the language of the error messages.

    Authenticate customer

    The /api/authenticatecustomer/ endpoint checks if a given customer id/email + password combination is valid, and return the id of the corresponding customer.

    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.

    Parameters

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

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

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

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

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

    Return value

    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"
    }

    or

    {
      "authenticated": false,
      "errorcode": "invalidpassword"
    }

    Pre-authentication token

    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 a useful building-block for a single-sign-on site.

    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.

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

    Iframes

    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.

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

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

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

    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.

    Paywall

    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.

    http://www.example.com/paywall/landing.html?iterasnext=...&iterasaccess=...

    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

      Iteras.logoutFromWall()

    e.g.

      <button id="logout">Log out</button><
    
      <script>
        document.getElementById("logout").onclick = function () {
          Iteras.logoutFromWall();
        };
      </script>

    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.

    IP-based access control

    Iteras internally has support for adding an IP access field to subscriptions.

    It has the effect that when the paywall login iframe is shown, a request is sent to the server and if the request IP address matches a subscription, the client is automatically logged in as the corresponding subscriber as if the client had submitted the password form. From then on, everything else works as usual.

    Note that IP-based access can be more difficult to control as it depends on specific firewall and network settings in the client end, compared to the standard paywall which simply depends on a working browser. Often clients are not well-equipped to analyze network problems - they just want access ASAP.

    We have a helpdesk article on diagnozing IP-related problems, but otherwise we cannot really help.

    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.

    sub|n8fsoupukhv3|2016-01-23T12:35:29Z|31168|123.123.123.123/sha256:596c625c185499e6a4394dbcec8713347ba7d0e67acb1fe3fe876f424f35bfc7

    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)
    • customer number
    • 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.