openapi: 3.1.1
info:
  title: iDIN api
  description: |
    [iDIN](https://idin.nl) is a service by the banks, that allows customers to identify themselves on websites, using the same secure methods as their own bank uses. It is similar to the iDEAL system in how it works and operates.

    In addition to identification, it can also provide the connecting website with information about name, address and age of the consumer, if the consumer agrees to provide these.

    CM provides a simple API to integrate these options into your website. If you have not yet received a merchant token, you can request one via [this link](https://www.cm.com/app/verification-registration/idin).

    ### How does it work ?
    - The merchant asks the customer to select his bank
    - Start the request for authentication/information
    - The customer is redirected to this bank
    - The customer logs into his bank and approves the transaction
    - The bank sends the customer back to the merchant's (your) landing page
    - The merchant rejoins the customer to his session and retrieves the transaction.
    - You check with the CM iDIN system if the transaction was successful and receive the requested customer information.

    ### Usage
    The iDIN system allows you to service several use cases
    * Checking if someone is known with a bank.
      * To see if the user is a legal entity known to a bank
      * To be able to trace the user in case of fraud.
    * Being guaranteed that this is always the same person. For instance
      * To log a user into your system
      * To avoid people registering multiple (fake) accounts in your system.
    * To check if a user is above a certain age limit
    * Retrieving name, address and age information of that person.
      * You should always allow the user to override or change this information,
        because it is not guaranteed that the information is always correct or complete
        (someone could have moved but not yet have informed his bank).
      * Match this against your own information and trigger audit signals

    ### Things you should not do:
    * Matching an account in your system on the basis of name/address attributes.
      * Either create a new account after a user identified with iDIN
      * or have the user log into your system before coupling with an iDIN identity

    ### Things you cannot do:
    * Check if an IBAN exists
    * Check if an IBAN belongs to an iDIN user
  version: "1.0"
servers:
- url: https://api.cm.com/idin/v1.0
tags:
- name: Directory
- name: Transaction
- name: Merchant
paths:
  /directory:
    post:
      operationId: list_banks
      tags:
      - Directory
      summary: Get bank list
      description: |
        Retrieve a listing of all the banks and their identifiers. The result is grouped by country. It is encouraged to cache this list, but you should refresh the list at least once a day.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DirectoryRequest'
        required: true
      responses:
        "200":
          description: List of banks
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DirectoryResponse'
        default:
          description: Any non-compliant request will return an error object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /transaction:
    post:
      operationId: create_transaction
      tags:
      - Transaction
      summary: Create transaction
      description: |
        Start an authentication or information request
      requestBody:
        description: Start
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransactionRequest'
        required: true
      responses:
        "200":
          description: Successful transaction
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionResponse'
        default:
          description: Any non-compliant request will return an error object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /status:
    post:
      operationId: get_status
      tags:
      - Transaction
      summary: Get transaction status
      description: |
        After the user has returned to you via your `merchant_return_url`,  you retrieve the `transaction_id` from the trxid parameter. You should check that both the `entrance_code` and the `transaction_id` match your expectations for that consumer, before you make this status call.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusRequest'
        required: true
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
        default:
          description: Any non-compliant request will return an error object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /merchants/{merchant_token}:
    get:
      operationId: get_merchant
      tags:
      - Merchant
      summary: Get merchant information
      description: |
        Retrieve information about a merchant
      parameters:
      - name: merchant_token
        in: path
        description: |
          a UUID string that is unique and private to you as a merchant. Do not share this key, keep it safe. Example 3c01abeb-b031-4fea-9f2d-c55c283cd78e
        required: true
        schema:
          type: string
          format: uuid
      responses:
        "200":
          description: An object with all information about this merchant
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MerchantResponse'
        default:
          description: Any non-compliant request will return an error object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    MerchantToken:
      type: string
      description: "a UUID string that is unique and private to you as a merchant. Do not share this key, keep it safe. Example 3c01abeb-b031-4fea-9f2d-c55c283cd78e"
      format: uuid
      examples: [ 3c01abeb-b031-4fea-9f2d-c55c283cd78e ]
    TokenizedRequest:
      title: TokenRequestParameters
      required:
      - merchant_token
      type: object
      properties:
        merchant_token:
          $ref: '#/components/schemas/MerchantToken'
    DirectoryRequest:
      $ref: '#/components/schemas/TokenizedRequest'
    DirectoryResponse:
      title: ArrayOfCountries
      type: array
      items:
        title: Country
        type: object
        properties:
          country:
            maxLength: 128
            minLength: 1
            type: string
            description: The readable name of the country
          issuers:
            title: ArrayOfIssuers
            type: array
            items:
              $ref: '#/components/schemas/Issuer'
    TransactionRequest:
      title: Transaction request
      description: |
        Use one or more of identity, name, address, date_of_birth and 18y_or_older to request information
      allOf:
      - $ref: '#/components/schemas/TokenizedRequest'
      - required:
        - entrance_code
        - issuer_id
        - merchant_return_url
        type: object
        properties:
          identity:
            type: boolean
            description: |
              Retrieve an identifying token (bin) with the bank for this consumer that is consistent across multiple
              sessions. When false returns a transient_id that will differ per transaction.
          name:
            type: boolean
            description: Retrieve the name information associated with this consumer
          gender:
            type: boolean
            description: Retrieve the gender of this consumer
          address:
            type: boolean
            description: Retrieve address information associated with this consumer
          date_of_birth:
            type: boolean
            description: Retrieve the birthdate of the user
          "18y_or_older":
            type: boolean
            description: Retrieve if this user is known to be 18 years or older.
          email_address:
            type: boolean
            description: Retrieve the email address associated with this consumer.
          telephone_number:
            type: boolean
            description: Retrieve the telephone number associated with this consumer.
          issuer_id:
            $ref: '#/components/schemas/IssuerID'
          entrance_code:
            maxLength: 40
            minLength: 1
            pattern: "^[a-zA-Z0-9]+$"
            type: string
            description: |
              This is a token that will allow you to rejoin the user to his session when he returns. It can be a maximum of 40 characters and should only contain the characters a-z, A-Z and 0-9. It should only be valid once and needs to be random enough (best use a cryptographically secure random generator) to avoid the possibility of replay attacks.
          merchant_return_url:
            maxLength: 512
            type: string
            description: |
              The URL the bank should redirect the user to at the end of the flow. The bank will append two query parameters to this URL when returning the user to you, `trxid` and `ec`. The latter will contain the value of entrance_code, trxid is the `transaction_id` that you will receive in this request.
          language:
            maxLength: 2
            minLength: 2
            type: string
            description: |
              The 2 character language code in which to return the results. Can be either 'nl' or 'en' for Dutch or English. This is a preferred language, not all banks support all languages.
            examples: [ nl ]
            enum: [ en, nl ]
          transaction_reference:
            maxLength: 255
            type: string
            description: |
              A custom reference you can provide that we will add to the transaction, making it possible for you to distinguish transactions.
            examples: [ 7defa5c6-7651-45cd-9016-f9027dc4dda9 ]
    TransactionResponse:
      title: Transaction response
      required:
      - issuer_authentication_url
      - merchant_reference
      - transaction_id
      type: object
      properties:
        transaction_id:
          $ref: '#/components/schemas/TransactionID'
        issuer_authentication_url:
          maxLength: 512
          type: string
          description: |
            The location with the issuing bank to which you should now forward the customer
          examples: [ https://issuerserver/transaction ]
        merchant_reference:
          maxLength: 35
          pattern: "^[a-zA-Z0-9]+$"
          type: string
          description: A reference id for accounting purposes
        transaction_reference:
          $ref: '#/components/schemas/TransactionReference'
    StatusRequest:
      title: StatusRequest
      allOf:
      - $ref: '#/components/schemas/TokenizedRequest'
      - title: StatusRequestParameters
        required:
        - merchant_reference
        - transaction_id
        type: object
        properties:
          transaction_id:
            $ref: '#/components/schemas/TransactionID'
          merchant_reference:
            maxLength: 35
            pattern: "^[a-zA-Z0-9]+$"
            type: string
            description: The private reference of the transaction
    StatusResponse:
      title: The results of the information requested.
      type: object
      properties:
        transaction_id:
          $ref: '#/components/schemas/TransactionID'
        transaction_reference:
          $ref: '#/components/schemas/TransactionReference'
        issuer_id:
          $ref: '#/components/schemas/IssuerID'
        status:
          $ref: '#/components/schemas/TransactionStatus'
        bin:
          maxLength: 256
          minLength: 6
          pattern: "^[a-zA-Z]{6,6}.*$"
          type: string
          description: "When identity was requested, this will be a consistent identifier for this user. You can use this to connect with another account of the user. If identity was not requested, then this will be equal to the transient_id."
          examples: [ NLINGB3x4u89498qe4tqjvdaj0 ]
        transient_id:
          maxLength: 256
          minLength: 6
          pattern: "^TRANS.{1,251}$"
          type: string
          description: "When identity was not requested, this will be a transient identifier for this user. It will be different for each request, but confirms that the person is known to the bank."
          examples: [ TRANS3x4u89498qe4tqjvdaj0 ]
        name:
          type: object
          properties:
            gender:
              type: string
              examples: [ female ]
              enum: [ male, female, unspecified ]
            initials:
              maxLength: 20
              type: string
              examples: [ A ]
            last_name:
              maxLength: 200
              type: string
              examples: [ Dijk ]
            last_name_prefix:
              maxLength: 10
              type: string
              examples: [ van ]
        address:
          type: object
          properties:
            street:
              maxLength: 43
              type: string
              examples: [ Dijklaan ]
            house_number:
              pattern: "^[0-9]{1,5}$"
              type: string
              examples: [ "1" ]
            house_number_suffix:
              type: string
              examples: [ b ]
            postal_code:
              pattern: "^[0-9]{4}[a-zA-Z]{2}$"
              type: string
              examples: [ 0000AA ]
            city:
              maxLength: 24
              type: string
              examples: [ Amsterdam ]
            country:
              maxLength: 2
              type: string
              examples: [ NL ]
        age:
          type: object
          properties:
            date_of_birth:
              type: string
              format: date
              examples: [ 1970-01-01 ]
            "18y_or_older":
              type: boolean
              examples: [ true ]
        telephone_number:
          maxLength: 20
          pattern: "^[0-9 ()+-]{1,20}$"
          type: string
          examples: [ "+31612345678" ]
        email_address:
          maxLength: 255
          type: string
          format: email
          examples: [ no_reply@example.com ]
      description: Note that not all information that was requested is guaranteed
        to be available from the bank and present in the response.
    MerchantResponse:
      title: Merchant information
      type: object
      properties:
        name:
          type: string
          description: The name of this merchant
          examples: [ Example merchant ]
        status:
          type: string
          description: |
            When you are not fully known to CM yet, and have not yet proven to qualify as an iDIN merchant, then you will have the `onboarding` status. In this status, you will only be able to retrieve test data.
          examples: [ onboarding ]
          enum: [ onboarding, active, disabled ]
        services:
          type: object
          properties:
            identity:
              type: boolean
              description: |
                Retrieve a uniquely identifying token with the bank for this consumer that is consistent across multiple sessions
            name:
              type: boolean
              description: Can retrieve the name information associated with this
                consumer
            gender:
              type: boolean
              description: Can retrieve the gender of this consumer
            address:
              type: boolean
              description: Can retrieve address information associated with this consumer
            date_of_birth:
              type: boolean
              description: Can retrieve the birthdate of the user
            "18y_or_older":
              type: boolean
              description: Can retrieve if this user is known to be 18 years or older.
            email_address:
              type: boolean
              description: Can retrieve the email address associated with this consumer.
            telephone_number:
              type: boolean
              description: Can retrieve the telephone number associated with this
                consumer.
          description: This lists the services that you are allowed to access per
            your agreement with CM.
        contact:
          type: object
          properties:
            name:
              type: string
              description: Name of contact person
            phone:
              type: string
              description: Phone number of contact person
            email:
              type: string
              description: E-mail address of contact person
              format: email
          description: Your contact information as it is known to CM
        balance:
          type: object
          properties: { }
          description: Reserved. This will list the credit balance etc.
      description: Retrieve properties of this merchant
    ErrorResponse:
      title: Error Response
      type: object
      properties:
        status:
          type: integer
          format: int32
          description: "Response status code, should match HTTP error code"
        message:
          type: string
          description: "High level description of the error that occurred. This description, when present, can be shown to the end user. It might describe that a bank is not available or that the system is offline, or that an unexpected error occurred. In test mode, the API will return more information (targeted at the developer) here then in production mode."
        code:
          type: integer
          format: int32
          description: Low level and/or internal error code describing the error.
      description: |
        Whenever an error occurs, you are expected to show the message to the user
        and to give the user the option of trying again. Errors can happen for a variety of reasons,
        but a common reason is because the issuer the user chose is temporarily not available.
    Issuer:
      type: object
      properties:
        issuer_id:
          $ref: '#/components/schemas/IssuerID'
        issuer_name:
          maxLength: 35
          minLength: 1
          type: string
          description: |
            The name of the issuing bank intended for display purposes. Used as the content of the <option> in HTML dropdowns.
          examples: [ Rabobank ]
    TransactionID:
      maxLength: 16
      minLength: 16
      pattern: "^[0-9]{16}$"
      type: string
      description: |
        A token for this transaction. You should store this with your session data, so that at any point, you can make a callback to the iDIN api and retrieve the status and/or results. Note that it is not guaranteed that your user will return to you via your merchant_return_url. A connection might be dropped, a user might accidentally close a window, or he might trigger the back button and return that way. This id is the only way you can retrieve any information in that case.
    TransactionReference:
      maxLength: 255
      type: string
      description: |
        The optional custom reference you provided when creating the transaction. If not provided, will return null.
      examples: [ 7defa5c6-7651-45cd-9016-f9027dc4dda9 ]
    IssuerID:
      pattern: "^[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}$"
      type: string
      description: |
        An identifier for the bank. Used as the value of the <option> in bank selector dropdowns. The end user selects a value and iDIN will direct the end user to that bank, so that the user can identify himself.
      examples: [ RABONL2U ]
    TransactionStatus:
      type: string
      description: |
        If the status is `open`, then the information was not yet available. Please try again after a few more seconds. If the status is `success` then you can expect the requested information to be present. Please note that personal information will only be returned on the first occurrence of a `success` status. A status of `cancelled` means that the user has cancelled the flow.
      examples: [ success ]
      enum: [ open, success, cancelled, failure, expired ]
