General use

Twikey has put in place several services enabling software partners, companies with own ERP or CRM systems, banks and other 3rd parties to exchange information.
This page describes the different calls that are available to you. The calls allow integration in a number of ways.

Since there are numerous ways a company can allow its customers to pay, we also provided a couple of high level use-cases to get you up and running in no time.

All exchanges require a valid AuthorizationToken passed on as a header named "Authorization".
The AuthorizationToken can be retrieved via the login call and is valid for 24h.
We suggest that the call to the login is done when the actual call you wish to make is returning a 401. This way you don't need to handle the lifecycle of your token yourself.

Exchanges can be in both JSON & XML responses. By default they will be in JSON, but by giving the "Accept" header a value of 'application/xml', you can get an xml payload.

Errors can be divided into 2 categories.
The first category are errors based on user input for which we return a 400 error with the code of the error mentioned in the response header "ApiError'.
The translated error can be found in the body of the required exchange. The language is provided by the "Accept-Language" header or defaults to English.
The second category of errors is one that you normally shouldn't run into a lot. In this case we return a 500 error which means that something went wrong on our end. We'll probably already know about it when you see the error.

Sample use cases

The fitness scenario

Imagine you're going to the fitness where you exercise every once in a while.
Since this doesn't come free you pay a monthly subscription fee.
This subscription fee is a recurring payment (we'll come back to that later).
But since all those exercises get you thirsty, you want to drink there too.
But of course, there are hotter and colder days. So how much you drink can vary.

Regardless of what you consume, the fitness wants to get paid.
But you'd like to avoid getting out your wallet when you're all sweaty.
So your fitness made the great decision to use direct debits to allow you to pay.
At the end of the month they add the recurring amount to the one-off's (the drinks).
They send the payment request to the bank. Everyone's happy and relax.

What did the fitness do to achieve this relaxed way of working:

First of all, everything starts with a mandate. When a customer enrolls at the fitness he fills in the details for the internal bookkeeping of the fitness. The same information is sent to the prepare call.
This returns a URL (representing an unsigned mandate) that opens up after entering all details in the enrollment screen.
Since the fitness has an internal subscription number they want to use to track payments and have
multiple subscriptions (eg. with and without personal trainer) they add both parameters in the prepare call. Making it visible in the mandate overview.

Once the customer filled in the account info and signed the mandate he's sent back to the fitness website with a "thank you" message.
When a customer ask for a drink at the bar, the cash register calls the transaction endpoint with the details and amount. These are one-off transactions. Recurring transactions can either be handled the same way or if a subscription parameter was added in the prepare call, a recurring transaction is automatically added every month to get this subscription paid. At the end of each month, the file is created and sent to the bank manually via the collect call or automatically every night.
Your bill is paid and the bank sends you the money. When the bank sends us back the account information, it is marked in your transaction overview that the transaction was paid. This information can be retrieved by the payment call. This call returns all new payment information since the last call. If a payment didn't succeed, you can configure what will happen next in the interface in the dunning section.

The parking app scenario

Because my customers don't like running in the rain looking for a parking meter while there's an app for that.
How can I use Twikey to get my parking fees paid? First of all, you need a mandate .
There are three possible options to have this mandate signed:

Use an in-app browser with the link retrieved via the prepare call
Use the sign call to invite the user to sign via an sms confirmation
Use the sign call adding the manual signature as a png-image in the payload
Once the mandate is signed, in-app purchases can be sent from the backend of the app, collected the same way as mentioned in the fitness scenario.

The (off-line) webshop scenario

I have a shop where people come in physically but I also have a webshop.
I want people who signed a mandate (either online via the above flow or physically) to be able to purchase something
from the online shop without requiring them to use their credit card and if possible give them the sameconvenience in the physical shop.
This way I can send them an invoice every month by only registering their purchases and collecting the payment via direct debit.

In the past, I had to send out all invoices and patiently waited for them to be paid.
Since I'm not the most patient person on earth and even a bit chaotic at times,
I'd rather collect the money directly from my customers. This way they can't forget to pay and I don't need to remind them to do so.
How convenient this is for both my customers and I.

Authentication

Login

When using the API the login call will provide you with an AuthorizationToken, which is to be send upon every subsequent call (via the authorization header) in order to use the api.
If the creditor opted for enhanced security, the private key allows the generation of a Time-based One-time password.
See Appendix C of the Creditor API documentation for a more detailed explanation
or sample code on how to calculate this OTP (samples in various languages are available).

Normally this call is made on request of another call that returned a 401 (UNAUTHORIZED) indicating that there was no session token or that it expired.

This AuthorizationToken acts as a session token and has a validity of 24h.

You must replace **API_KEY** with your personal API key which can be found in the Twikey Creditor dashboard.

HTTP Request

POST https://api.twikey.com/creditor

Query Parameters

Name

Description

Required

Type

apiToken

API key

Yes

string

otp

Value calculated based on salt and private key (if enhanced security)

No

long

HTTP Response

Code

Description

200

For security reasons, the http status is always 200, however for returning error codes specific to the call 2 additional response headers were added "ApiError" and "ApiErrorCode" which are added on every response that contains errors. Notible exceptions are authentication errors and authorisation errors that don't have these 2 headers.

Logout

Invalidates the AuthorizationToken by making a GET request to /creditor

HTTP Request

GET https://api.twikey.com/creditor

HTTP Response

Code

Description

204

Logged out succesfully

Mandate

Invite a customer

Necessary to start with an eMandate or to create a contract. The end-result is a signed or protected shortlink that will allow the end-customer to sign a mandate or contract.
The (short)link can be embedded in your website or in an email or in a paper letter. We advise to use the shortlink as the data is not exposed in the URL's.
The parameters are as described in detail on the page "Create contracts".

After signing the end-customer is either presented with a thank-you page or is redirected to an exit url defined on the template.
This url can contain variables that are filled in depending on the outcome. See exit urls

the contract number which can override the one defined in the template.

No

string

campaign

Campaign to include this url in

No

string

prefix

Optional prefix to use in the url (default companyname)

No

string

check

If an existing collectable mandate exists, don't prepare a new (based on email + template(=ct))

No

boolean

reminderDays

Send a reminder if contract was not signed after number of days

No

number

sendInvite

Send out invite email directly

No

boolean

document

Add a contract in base64 format

No

string

amount

in cent, to initiate a first payment (PSP needs to be activated)

No

string

}

HTTP Response

Code

Description

200

If the check option is provided and an existing collectable mandate is available it will be returned. Otherwise an url and key will be returned.

400

User error if parameter is given but not valid or collectable (available in apierror header and response)

Error codes

Code

Description

err_no_such_ct

No template found (or not active)

err_invalid_domain

Invalid domain

err_mandatenumber_required

No mandatenumber was given, while setting does not allow generation

err_double_mandatenumber

Signed mandate with the same mandatenumber already exists

err_missing_params

Attributes are missing while configured as mandatory

Sign a mandate

Create a contract with an invitation/signature directly via API. Note that this call can require different parameters depending on the method of signature. All parameters are described in Create contracts
When enabled for your contract it is possible to negiotiate mandates with their signature directly via API. Depending on the method the set of required parameters and/or handling may differ.
Methods currently supported :

sms: require the mobile parameter

digisign a base64 encoded png should be provided as payload. (max 150 k)

the contract number which can override the one defined in the template.

No

string

signDate

Date of signature (xsd:dateTime), sms uses date of reply

No

string

place

Place of signature

No

string

HTTP Response

Code

Description

200

The request has succeeded

400

User error if parameter is given but not valid (available in apierror header and response)

Error code

Code

Description

err_no_such_ct

No template found (or not active)

err_invalid_domain

Invalid domain

err_mandatenumber_required

No mandatenumber was given, while setting does not allow generation

err_double_mandatenumber

Signed mandate with the same mandatenumber already exists

err_missing_params

Attributes are missing while configured as mandatory

err_invalid_signature

No valid method was provided

Update Feed

Returns a List of all updated mandates (new, changed or cancelled) since the last call.
From the moment there are changes (eg. a new contract/mandate or an update of an existing contract) this call provides all related information to the creditor.
The service is initiated by the creditor and provides all MRI information (and extra metadata) to the creditor.
This call can either be triggered by a callback once a change was made or periodically when no callback can be made.
This information can serve multiple purposes:

Updating a CRM system to take appropriate actions (eg. call the customer on cancel..)

Integrate the info in an ERP system to create the correct SDD files (in case of mandates)

If your system is out of sync or the full mandate database needs to be refreshed an optional "X-RESET" header can be passed.
The format can be a timestamp like "2015-08-20T13:02:03Z". After that you will receive (all) mandate updates since that time again.
In order to make a split between types of documents you can pass "X-TYPES" in the header with following parameters in upper case only. (CONTRACT - CORE - B2B)

In order too avoid polling, a #19-callbacks can be setup to notify the client when new information is available. This hook can be configured in the Settings > API.

There are 3 possible updates.

The first one is when a new signed mandate is available.

The second is an amendment on a mandate (like address change). Indicated by the existence of an "AmdmntRsn".

Last one is a cancellation of a mandate. Indicated by the existence of a "CxlRsn"

HTTP Request

Query Parameters

Name

Description

Required

Type

Mandates

All mandate updates like in fetch or update feed (json)

Yes

HTTP Response

Code

Description

204

Batch import of mandates was fully done or not at all

Cancel a mandate

A contract can be cancelled by either debtor or creditor, this can be done via the website or the api.
However, there may be circumstances in which the creditor receives the cancel not through Twikey. In order to avoid extra costs & stale information,
Twikey requires the update of its systems. The creditor, the creditor bank or the debtor bank, can initiate this request. Afterwards the update is distributed to all parties.

Query Parameters

Response

User error if parameter is given but not valid (available in apierror header and response)

429

Too many requests

Error codes

Code

Description

err_no_contract

No contract was selected or invalid mndtId supplied

err_no_contract

No contract was found

err_mandate_invalid_state

Contract was not signed (ignored when force=true)

Customer access

You may want to give your customer access to the mandate details without actually requiring him to
get a Twikey account. You can do this by using this call. This call returns a url that you can redirect
the user to for a particular mandate.

HTTP Request

POST https://api.twikey.com/creditor/customeraccess

Query Parameters

Name

Description

Required

Type

mndtId

Mandate Reference

Yes

string

Update planned transactions

Sometimes a plan on an existing mandate needs to be updated. This endpoint allows to update a plan, just by passing the new plan data.
If the plan requires a custom plan,all parameters need to be send.

Request Parameters

Message to the customer (if only 1 entry) or structural message if 12 characters long

Yes

string

ref

Your reference

No

string

amount

Amount to be billed

Yes

string

place

Optional place

No

string

HTTP Response

Code

Description

200

The request has succeeded

400

User error if parameter is given but not valid (available in apierror header and response)

Error codes

Code

Description

err_no_contract

No mandate found

err_mandate_invalid_state

The mandate is not active

err_invalid_date

Invalid Date

err_invalid_sepachars

Invalid characters in message to debtor

err_invalid_amount

Invalid Amount given

err_billing_overdrawn

Maximum amount reached according to risk rules

Transaction feed

Retrieve list of transactions that had changes since the last call.
If your system is out of sync or the full transaction database needs to be refreshed an optional "X-RESET" header can be passed. The format can be a timestamp like "2018-08-20T13:02:03Z". After that you will receive (all) transaction updates since that time again.

This endpoint allows to retrieve a list of all the transactions for which new payment information has been received since the last call. This endpoint doesn't require any parameters.
If we receive an error from the bank, we mark the transaction with status "error" or "paid". We also add a final flag which can either be true or false.
True (being final) means that we can't do anything with it anymore. This could be the case for paid transactions as well as for errors like debtor deceased. An action will be required on your part.
False means that we still have actions pending to debit the debtor's account.

Possible states

Possible states

Description

OPEN

The transaction is created but not processed by the bank

PAID

The transaction has been executed + final flag info

ERROR

The transaction has not been executed + final flag info

Final flag state

Possible states

Description

TRUE

An action required from your part, paid or error like f.e. debtor deceased

This endpoint allows to retrieve a list of SDD's for which new payment information is given.
If the parameter 'id' is passed, it will also return the details of the mandates contained in the referenced SDD.
If the 'detail' parameter is passed (regardless of its value) then the call with the 'id' parameter is obsolete as the details are included in the general call.
In case we receive an error from the bank, we mark the transaction with status "error" or "paid", but on top of it we add a final flag which will either be true or false.
True (being final) means that we can't do anything with it anymore. This could be the case for paid transactions as well as for errors like debtor deceased. An action would be required from your part.
False means that we still have actions pending to debit the debtor's account.

Possible states

Possible states

Description

OPEN

The transaction is created but not processed by the bank

PAID

The transaction has been executed + final flag info

ERROR

The transaction has not been executed + final flag info

Final flag state

Possible states

Description

TRUE

An action required from your part, paid or error like f.e. debtor deceased

FALSE

More actions are pending to debit the debtor's account

Paymentlinks

Paymentlinks can facilitate a one-off payment that needs to happen or can be used in the dunning process. For the latter you don't need to use these endpoints. However if you want to use the former, these endpoints will be of service to you.
We don't provide a list endpoint as updates should be retrieved upon receiving a webhook.

Create paymentlink

Create a payment link for an affiliated customer (via email) or via a name (in which case no customer is linked).
It is possible to create a -consolidated- paymentlink by using the parameter txref.
Unique transaction references are required!
In this case you create a paymentlink linked over several transactions based on their transaction reference. Once paid, every transaction will be marked as paid as well.

Request Parameters

Name

Description

Required

Type

all

Include all non-paid updates too (by default only paid updates are returned)

No

boolean

HTTP Response

Code

Description

200

The request has succeeded

Refunds (Credit Transfer)

Refunds can be used to completely or partially refund a transaction to a debtor.
In exceptional cases, you may have to transfer funds from one account to another. This could be the case when you collected money that should be transferred to another person or entity.
In this case, you have to:

define the beneficiary of the refund

create the refund

create the batch

download the batch in the Twikey GUI and process the batch in your bank environment

Create/add a new credit transfer

Create or add a new credit transfer. This can only be done after the creation of the beneficary.

Query parameters

HTTP Response

User error if parameter is given but not valid (available in apierror header and response)

Error codes

Code

Description

err_no_transaction

No such transaction

Batch creation

Once the credit transfers are created, you need to create the batch with refunds to send to the bank.
A batch can be created based upon the account from an existing contract template or new account.
After the creation of the batch, the file needs to be processed in your bank environment.
For some banks we can do the upload of the batch in your bank environment, for other banks you will need to download the batch from the Twikey dashboard via the menu Refunds.
A refund must always be signed extra in your bank environment.

Query parameters

HTTP Response

Code

Description

200

The request has succeeded

400

Invalid request

Error codes

Code

Description

err_invalid_params

No such batch available

Get credit transfer feed

Retrieve list of credit transfers that have changes since the last call.
If your system is out of sync or the full transaction database needs to be refreshed an optional "X-RESET" header can be passed. The format can be a timestamp like "2018-08-20T13:02:03Z". After that you will receive (all) transaction updates since that time again.

Query parameters

Name

Description

Required

Type

ct

Template containing the originating account

Yes

string

bic

BICCode of the bank to retrieve a url for

Yes

string

HTTP Response

Code

Description

200

The request has succeeded

Webhooks

In order to reduce the number of polling requests, one can opt to implement a webhook endpoint and use this as a way to trigger polling requests.
The endpoint is set in the API section of the settings screen and will convey information about various events in Twikey.
The call is done via a simple GET with basic (non-sensitive) parameters eg. http://my.company.com/callback?type=contract&mandateNumber=MNDT123&state=signed...

In order to verify that the request is indeed coming from Twikey you can verify the headers 'apiToken' and optionally 'otp' which should hold the same values
as you'd use for the login.

Note that the url to be given in the interface is without parameters since these are filled up depending on the type.

type=contract

Triggered when something happens to the mandate. Activation events (when a mandate becomes suspended or gets resumed) will be
included in the event=Update with the reason being either 'resumed" or 'suspended'

type=plan

type=payment

Triggered when a batch of payments were received. Note that this is triggered for the batch and not per transaction as
it could cause a flood of requests.

No parameters to avoid hammering your site when new transaction feedback is received

This is also triggered when a paymentlink was updated to either paid, rejected or expired. In that case
you will receive the initial id, ref and status back.

type=dunning

Triggered when a dunning action was executed for a failed transaction

Extra parameters:

mandateNumber : Mandatenumber

txIds : Transaction ID

Exit Url

An alternative to a webhook can be the exit url or the page where the end-user is being redirected to after
signing. This is a thank-you page or a redirect to an exit url defined on the template.
This url can contain variables that are filled in depending on the outcome. The variables can either be positional of named.