General use
Twikey has set up an api enabling software partners, companies to integrate with their own systems. As there are numerous ways in which a business can make its customers pay, we have also provided a few high-level usage examples to get you started in no time.
Integration architecture
We support 2 kinds of integrations. With a pull architecture, clients fetch updates from Twikey to get the latest state within Twikey. This architecture is preferred if you don't have any existing webservers or not on the machine requesting the updates. However, the downside is that there is a delay between the moment an event occurs in Twikey and the moment you receive the event.
The alternative is a push-fetch architecture where Twikey notifies you about an event via a webhook. You can use this webhook to retrieve the events and update your internal systems. These 2 main types of architecture also occur for some hybrid solutions where hooks are combined with recurrent pulls.
Feeds & webhooks
Status changes are much more common for agreements, invoices and transactions than for regular one-off payments. Since we don't want you to miss any event and multiple webhooks might not arrive in the correct order due to network latency, we provide "feeds" which are actually a list of events you haven't received yet. By reading this feed every so often (polling) or after receiving a webhook, you can be sure have received all events available in Twikey.
As security is essential for payments, Twikey has decided not to pass on details about payments in webhooks, but only to provide them in requests coming from your end. We allow specific "detail" calls about an object but since they are only a snapshot of the object at that time (and might already have changed by the time you've handled it) we've added rate-limits to avoid using them.
Guidelines
- All exchanges require a valid token passed on as a header named "Authorization". The token can be retrieved via the login call and is valid for 24h. We suggest that the login call takes place when the actual call is returning a 401. By doing this, you don't need to handle the lifecycle of your token yourself.
- Errors can be divided into 2 categories. The first category are errors for which we mention a 400 error code in the response header "ApiError'. The error is explained in 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.
- Webhooks are notification and should be treated as such. As there is a timeout on webhooks, internal business logic followed should be done synchronous to avoid resending the same event multiple times.
- Exchanges can be 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.
Idempotency Keys
A request can return different status codes such as 200 meaning all is well or a 400 when you know the call did not succeed. But what when it is crucial that a certain request doesn't hit our system twice, when due to some networking issue for example you'd ideally want to retry?
To provide a way to ensure each request you make is unique we support the standard idempotency header for our creation endpoints of transactions, refunds and subscriptions.
Idempotency-Key: "your unique reference"
Regardless if the request succeeded, failed or succeeded but wrong parameters were passed and we returned an error, you can only use the request once as the idempotency-key can only be used once and ensure that no duplicates are created.
We already supported idempotency implicitly for example trying to cancel the same mandate multiple times. This would have no adverse effect. From now, you will receive a response 409 (Conflict) when using the idempotency header with the same value more then once while a response is not yet available and you are trying to use the same key in your request(s).
Once we can return the response from your original request, that is returned upon using the same idempotency-key. The same body and HTTP code from your original request is returned.
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 enhanced security is setup, the private key allows the generation of a Time-based One-time password. View our sample code snippets on how to calculate this OTP in various languages. Please do not pass the apiToken as a query parameter in the url as it exposes the apiKey. Instead pass it into the body of the request.
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.
Find the API Key and security configuration in your Twikey Dashboard under Settings: Api
curl -X POST https://api.twikey.com/creditor /
-d apiToken=**API KEY**
$host = "https://api.twikey.com";
$apitoken = "**API_KEY**";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"apiToken=$apitoken");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
$authorization = $result->{'Authorization'} ;
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
apitoken = "**API_KEY**",
authorization = null,
options = {
host: host,
port: '443',
path: '/creditor',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body:{
'apiToken' : apitoken
}
};
var req = https.request(options, function (res) {
authorization = res.headers.authorization;
console.log("authorization : ",authorization);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
apiToken = "**API_KEY**";
public void logIn(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("apiToken", apiToken)
.build();
Request request = new Request.Builder()
.url(host + "/creditor")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
apiToken ="**API_KEY**";
public void logIn(){
RestClient client = new RestClient(host + "/creditor");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"apiToken=" + apiToken,
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
{
"Authorization": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
}
This AuthorizationToken acts as a session token and has a validity of 24h.
HTTP Request
POST https://api.twikey.com/creditor
Form 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. |
Error codes
Code | Description |
---|---|
err_no_login | Not logged in |
Logout
Invalidates the AuthorizationToken by making a GET request to /creditor
curl https://api.twikey.com/creditor \
-H 'authorization: authorization'
$host = "https://api.twikey.com";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setOpt($ch, CURLOPT_HTTPHEADER, array(
"authorization: $authorization"
);
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
options = {
host: host,
port: '443',
path: '/creditor',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
});
public class TwikeyAPI {
private String host = "https://api.twikey.com";
public void logOut(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "creditor")
.get()
.addHeader("cache-control", "no-cache")
.addHeader("Authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyApi {
private String host = "https://api.twikey.com";
public void logOut(){
RestClient client = new RestClient(host + "/creditor");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
GET https://api.twikey.com/creditor
HTTP Response
Code | Description |
---|---|
204 | Logged out successfully |
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
Requires a ct_id which can be found in the Twikey Creditor Template overview
curl -X POST https://api.twikey.com/creditor/invite \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'authorization: **authorization**' \
-d 'ct=**ct_id**' \
-d 'l=nl' \
-d 'email=support@twikey.com' \
-d 'lastname=Support' \
-d 'firstname=Twikey' \
-d 'mobile=32479123123' \
-d 'address=Stationstraat 43' \
-d 'zip="9051"' \
-d 'city=Sint Denijs Westrem' \
-d 'country=BE'
$host = "https://api.twikey.com";
$ct = **ct_id**;
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/invite");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"ct=$ct"
."&l=nl"
."&email=support%40twikey.com"
."&lastname=Support"
."&firstname=Twikey"
."&address=Stationstraat%2043"
."&zip=9051"
."&city=Sint%20Denijs%20Westrem"
."&country=BE"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
$url = $result->{'url'} ;
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
ct = "**ct_id**",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/invite',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
data: {
"ct": ct,
"l": "nl",
"email": "support@twikey.com",
"lastname": "Support",
"firstname": "Twikey",
"address": "Stationstraat 43",
"zip": "9051",
"city": "Sint Denijs Westrem",
"country": "BE"
}
};
var req = https.request(options, function (res) {
res.on('data', function (chunk) {
console.log("Redirect to : " + chunk)
});
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String ct = "**ct_id**";
private String authorisation = null; //collected through logIn
public void invite(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("ct", ct)
.add("l", "nl")
.add("email", "support@twikey.com")
.add("firstname", "Twikey")
.add("lastname", "Support")
.add("address", "Stationstraat 43")
.add("zip", "9051")
.add("city", "Sint-Denijs-Westrem")
.add("country", "BE")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/invite")
.post(formBody)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Authorization", authorisation)
.addHeader("Cache-Control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct ="**ct_id**",
authorisation = null; // collected through logIn
public void invite(){
RestClient client = new RestClient(host + "/creditor/invite");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"ct=" + ct +
"&l=nl" +
"&email=support%40twikey.com" +
"&lastname=Support" +
"&firstname=Twikey" +
"&address=Stationstraat%2043" +
"&zip=9051" +
"&city=Sint%20Denijs%20Westrem" +
"&country=BE"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"mndtId": "COREREC01",
"url": "http://twikey.to/myComp/ToYG",
"key": "ToYG"
}
HTTP Request
POST https://api.twikey.com/creditor/invite
Query Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
ct | Contract template to use | Yes | integer | |
l | Language (en/fr/nl/de/pt/es/it) | No | string | 2 |
iban | International Bank Account Number of the debtor | No | string | 35 |
bic | Bank Identifier Code of the IBAN | No [*6] | string | 11 |
mandateNumber | Mandate Identification number (if not generated) [*1] | No | string | 34 |
customerNumber | The customer number (strongly advised) | No | string | 50 |
Email of the debtor | No | string | 70 | |
lastname | Lastname of the debtor | No | string | 50 |
firstname | Firstname of the debtor | No | string | 50 |
mobile | Mobile number required for sms (International format +32499123445) | No | string | 50 |
To update the address all address fields parameters are required | Yes | - | - | |
address | Address (street + number) | No | string | 70 |
city | City of debtor | No | string | 50 |
zip | Zipcode of debtor | No | string | 50 |
country | ISO format | No | string | 2 |
companyName | The company name (if debtor is company) | No | string | 140 |
vatno | The enterprise number (if debtor is company) | No | string | 50 |
contractNumber | The contract number which can override the one defined in the template. | No | string | 35 |
campaign | Campaign to include this url in | No | string | 250 |
prefix | Optional prefix to use in the url (default companyname) | No | string | 20 |
check | If a mandate already exists, don't prepare a new one (based on email, customerNumber or mandatenumber and + template type(=ct)) | No | boolean | |
ed | Expiry of the link (max.12 months in the future). Epoch timestamp in milliseconds | No | number | |
reminderDays | Send a reminder if contract was not signed after number of days | No | number | |
sendInvite | Send out invite directly [*2] | No | boolean/string | |
token | (optional) token to be returned in the exit-url (lenght<100) Percent Encoded characters are not supported | No | string | |
requireValidation | Always start with the registration page, even with all known mandate details | No | boolean | |
document | Add a contract in base64 format | No | string | |
transactionAmount | In euro for a transaction via a first payment or post signature via an SDD transaction | No | string | |
transactionMessage | Message for the transaction [*3] | No | string | |
transactionRef | Reference of the transaction [*4] | No | string | |
plan | Name of the the plan. see special cases Add a plan on invite below for more information. | No | string | |
subscriptionStart | Start date of the subscription (yyyy-mm-dd). This is also the first execution date. Required if adding a subscription | No | date | |
subscriptionRecurrence | 1w / 1m / 2m / 3m / 6m /12m (default 1m) | No | string | |
subscriptionStopAfter | Number of times to execute the subscription | No | number | |
subscriptionAmount | Amount for every transaction | No | number | |
subscriptionMessage | Message to the subscriber | No | string | |
subscriptionRef | Unique Reference of the subscription (important for updates) [*5] | No | string |
[*1] The mandateNumber is mandatory if you enabled the 'Never generate a mandate reference' in the template settings under 'Options'. The mandateNumber cannot use the same prefix as your template to avoid conflicts.
[*2] sendInvite can have a boolean or a string value, both are accepted:
- true: Invite is sent by email (requires an active email integration).
- letter: Invite is sent by letter
- letterWithMandate: Invite is sent by letter incl. the mandate
[*3] The transaction reference is either:
- The payment link remittance when the first payment is via a payment link
- The transaction reference when the first payment is via a transaction
[*4] The transaction description is either:
- The payment link title (displayed on screen when the customer is on the payment page) when the first payment is via a payment link
- The transaction communication when the first payment is via a transaction
[*5]
- The reference is a unique identifier for each subscription. This allows the subscription to be referred to when using a request.
[*6]
- The BIC code can be derived in most cases and is not mandatory. In some cases (foreign IBAN's) we can not derive the BIC code
and return the error
err_invalid_bic
. In this case pass the BIC code in your request.
Special cases:
Recurring Credit Card
For this you have to include an amount, the 'ct' should contain the number of a template of type credit card and optional a parameter 'method' (mastercard/visa) in the request.
The first payment to sign this type of document is always a payment link. Retrieve the mandate feed using a x-types header and/or the payment link feed to fetch details.
- Recurring Credit Card - Paypal via CCV
You need a Credit Card profil with the signing method 'Paypal' enabled (contact our support to enable this).
In your API request include the additional attribute method
with value paypal
- Add a plan on invite
When adding a subscription to a mandate it might be interesting to use a parent plan (in order to slice and dice your subscriptions). It also allows to change the base parameters of the subscription without the need to change your code.
To add a subscription based on a plan multiple options can be used:
- Put a default plan on a profile. Go to the profile > Payments > Default plan and select a plan. Note: This will always add a subscription when an agreement is signed.
- Or, if you do not have a default plan configured, add an attribute named 'plan' and of type 'plan' in the profile.
To pass a custom subscription on invite you need to pass the plan parameter using the name of an existing plan as value. This serves only as a base
the subscription will use the values (subscriptionRecurrence
, _subscriptionStart
, etc..._) you passed using the subscription parameters in the request.
The subscription is created once the end customer signs.
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_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_contract_signed | Specified contract is already signed |
err_invalid_mandatenumber | Reference or prefix is invalid |
err_bic_not_sepa | BIC code does not belong to a SEPA bank |
err_duplicate_ref | Duplicated reference |
err_invalid_bic | BIC code invalid |
err_invalid_coc | Invalid enterprise number |
err_invalid_country | Country code invalid |
err_invalid_email | Email invalid |
err_invalid_iban | IBAN invalid |
err_invalid_lang | Language code invalid |
err_too_long | The value of a parameter is longer then allowed |
err_email_disabled | No email integration is configured or all emails are disabled. Unable to send the invitation |
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 negotiate mandates with their signature directly via API. Depending on the method the set of required parameters and/or handling may differ. Methods currently supported :
- bancontact (or bcmc)
- paypal: Only for CCV Recurring credit card profiles with signing method Paypal enabled
- sms: require the mobile parameter
- itsme
- paper: preview the document or send a print invite via email
*'print the document and return the signed document via e-mail or upload.' must be enabled in the template under 'options'.
- Include the parameter 'sendInvite: true' to directly send the invitation via email.
- When 'sendInvite' is not passed or false, nothing is sent out.
- The response includes a preview of the PDF in base64 format.
- import: import the mandate with a specific signdate
- Either reference to an existing customer using
email
orcustomerNumber
- For new customers you need to pass the address parameters( address, city, zip, country)
- B2B: When using the method 'import' for a B2B profile, the mandate is directly signed. To require bank validation use
bankSignature: false
- Either reference to an existing customer using
- digisign: wet signature encoded as a base64 png (max 150 k)
- emachtiging, idin: requires the bic parameter You can retrieve the connected banks and bic codes using fetch connected banks
Credit cards:
- ct: template id of type creditcard
- methods:
- visa
- mastercard
- supported: CCV / Mollie and MultiSafepay
curl -X POST https://api.twikey.com/creditor/sign \
-H 'authorization: **authorization**' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'method=sms' \
-d '&place=Gent' \
-d '&ct=**ct_id**' \
-d '&iban=BE68068897250734' \
-d '&bic=GKCCBEBB' \
-d '&email=sms%40twikey.com' \
-d '&lastname=Twikey' \
-d '&firstname=SMS' \
-d '&mobile=%2B32479123123' \
-d '&address=Stationstraat%2043' \
-d '&city=Gent' \
-d '&zip=9051' \
-d '&country=BE'
$host = "https://api.twikey.com";
$ct = **ct_id**;
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/sign");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"method=sms"
."&place=Gent"
."&ct=1323"
."&iban=BE68068897250734"
."&bic=GKCCBEBB"
."&email=sms%40twikey.com"
."&lastname=Twikey"
."&firstname=SMS"
."&mobile=%2B32479123123"
."&address=Stationstraat%2043"
."&city=Gent"
.'&zip=9051'
."&country=BE"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
$mandateId = $result->{'MndtId'} ;
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
ct = "**ct_id**",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/sign',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
data: {
"method": "sms",
"place": "Gent",
"ct": ct,
"l": "nl",
"email": "support@twikey.com",
"lastname": "Support",
"firstname": "Twikey",
"mobile": "+32479123123",
"address": "Stationstraat 43",
"zip": "9051",
"city": "Sint Denijs Westrem",
"country": "BE"
}
};
var req = https.request(options, function (res) {
res.on('data', function (chunk) {
console.log("manatenumber: " + chunk)
});
});
public class TwikeyApi{
private string host = "https://api.twikey.com";
private String ct = "**ct_id**";
private String authorisation = null; //collected through logIn
public void inviteAndSign(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("ct",ct)
.add("method", "sms")
.add("place"="Gent")
.add("mobile","+32479123123")
.add("l","nl")
.add("email","support%40twikey.com")
.add("lastname","Support")
.add("firstname","Twikey")
.add("address","Stationstraat%2043")
.add("zip","9051")
.add("city","Sint Denijs Westrem")
.add("country","BE")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/sign")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct ="**ct_id**",
authorisation = null; // collected through logIn
public void inviteAndSign(){
RestClient client = new RestClient(host + "/creditor/sign");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"method=sms" +
"&place=Gent"
"&ct="+ ct +
"&iban=BE68068897250734" +
"&bic=GKCCBEBB" +
"&email=sms%40twikey.com" +
"&lastname=Twikey" +
"&firstname=SMS" +
"&mobile=%2B32479123123" +
"&address=Stationstraat%2043" +
"&city=Gent" +
"&zip=9051" +
"&country=BE" +
, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"MndtId": "MyMandateId"
}
// Sign method: Itsme
{
"MndtId": "MNDT123",
"url": "https://e2emerchant.itsme.be/oidc/authorization?response_type=code&client_id=M48..."
}
HTTP Request
POST https://api.twikey.com/creditor/sign
Query Parameters
Same parameters as the invite call +
Name | Description | Required | Type | Max. |
---|---|---|---|---|
method | Method to sign (sms/digisign/import/itsme/emachtiging/paper,...) | Yes | string | |
digsig | Wet signature (PNG image encoded as base64) required if method is digisign | No | string | |
key | shortcode from the invite url. Use this parameter instead of 'mandateNumber' to directly sign a prepared mandate. | No | string | 36 |
bic | Required for methods emachtiging and iDIN | No | string | 11 |
signDate | Date of signature (xsd:dateTime), sms uses date of reply | No | string | |
place | Place of signature | No | string | |
bankSignature | For B2B only, require bank validation if set on false . The value is true by default. |
No | boolean |
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_such_ct | No template found (or not active) |
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 |
err_bic_not_sepa | BIC code is not SEPA |
err_contract_signed | Contract is already signed |
err_not_authorised | sign method not authorized |
err_invalid_bic | BIC code is invalid |
err_invalid_email | Email is invalid |
err_invalid_iban | IBAN is invalid |
err_invalid_mobile | Mobile number format is invalid |
smsErrorSending | Error sending SMS |
smsFixed | Phone number is fixed instead of mobile |
smsPendingContract | Mobile number currently in use and awaiting response from customer |
err_contact_support | Configuration problem, contact support |
Mandate 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)
In order too avoid polling, a webhooks 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"
Amendments and cancellation:
- Updates on unsigned (prepared) agreemenets are not included
- Retrieving the feed after a mandate was signed and cancelled will only return the event for the cancel.
Messages - SplmtryData
Each Message consists of a Mndt object containing all basic information such as merchant information (Cdtr), the customer information (Dbtr), mandate details, ..
The SplmtryData contains all non-structured data: custom attributes, plans, signer(s), ..
By default all attributes are returned, but this can be changed using the include
parameter.
See Query Parameters for all possible values.
Messages - EvtId
For each Message we also return the event date and time. This can be extended by the event id.
The event id is a sequence number - but is not sequential necessarily in your feed - used as identifier for the message.
To return the event identifier you can use the include parameter: include=seq
When you use include parameters, only those are returned.
See Query Parameters for all values.
Signed, Updated, Cancelled
Signed
A signed mandate will be returned in the feed including all the mandate, creditor, debtor details and the supplementary data. Supplementary data returns several keys: Each person that signed the document will be returned for each key using consecutive numbers #0, #1, .. .
- The signer(s) name: Signer#0, Signer#1, ..
- The method used by each signer: SignerMethod#0
- The place: SignerPlace#0 (city name, imported, ..)
- The date: SignerDate#0
This is returned in the "SplmtryData[key|value]" array.
Updated
When a document is updated, it is returned in the feed as amendment with initiator and reason:
- AmdmntRsn
- Orgtr (contains the details about the initiator)
- Rsn
The "Rsn" value informs you about the change, this can be information on the mandate that was updated or information about the mandate state. Examples:
- Rsn: uncollectable|user (mandate was suspended by a user)
- Rsn: collectable (mandate was activated, collectable again)
- Rsn: _T50 (specific T5 codes - see Types: Specific codes)
- Rsn: key|value (a (custom) attribute value was changed or added)
- This can be a 'plan' for example or a custom attribute 'amount'
- E.g.: "change|amount=5000" (attribute "amount" was changed to a new value)
- Rsn: _DUN (mandate is cancelled due to a dunning step)
Cancelled
When a mandate was cancelled, this is returned as Cancelled reason with initiator and reason:
- CxlRsn
- Orgtr (contains the details about the initiator)
- Rsn
The "Rsn" value can be a custom reason the user, bank or debtor entered on cancellation. This can also contain the reason of cancellation in case of automated dunning steps. Examples:
- Rsn: Transaction disputed (automated dunning step to cancel the mandate in this case)
- Rsn: custom reason (by user, customer,..)
curl https://api.twikey.com/creditor/mandate \
-H 'authorization: **authorization**' \
$host = "https://api.twikey.com";
$ct = **ct_id**;$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/mandate',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void updateFeed(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/mandate")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void updateFeed(){
RestClient client = new RestClient(host + "/creditor/mandate");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"GrpHdr": {
"CreDtTm": "2021-04-09T12:49:46Z"
},
"Messages": [
{
"Mndt": {
"MndtId": "B2B38434",
"LclInstrm": "B2B",
"Ocrncs": {
"SeqTp": "RCUR",
"Frqcy": "ADHO",
"Drtn": {
"FrDt": "2021-04-09"
}
},
"CdtrSchmeId": "BE81ZZZ1234567891",
"Cdtr": {
"Nm": "Merchant Company",
"PstlAdr": {
"AdrLine": "Companystreet 100",
"PstCd": "1000",
"TwnNm": "Brussel",
"Ctry": "BE"
},
"Id": "BE0123456789",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "merchant.email@example.com"
}
},
"Dbtr": {
"Nm": "Twikey NV",
"PstlAdr": {
"AdrLine": "Stationstraat 43",
"PstCd": "9051",
"TwnNm": "Gent",
"Ctry": "BE"
},
"Id": "BE0533800797",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "support@twikey.com",
"Othr": "custNumber001"
}
},
"DbtrAcct": "BE31798258915655",
"DbtrAgt": {
"FinInstnId": {
"BICFI": "GKCCBEBB",
"Nm": "BELFIUS BANK"
}
},
"RfrdDoc": "Terms and Conditions",
"SplmtryData": [
{
"Key": "SignerMethod#0",
"Value": "maestro"
},
{
"Key": "Signer#0",
"Value": "Mock Maestro"
},
{
"Key": "SignerPlace#0",
"Value": "Ghent"
},
{
"Key": "SignerDate#0",
"Value": "2021-04-09T13:18:19Z"
}
]
},
"EvtTime": "2021-04-09T13:19:19Z"
},
{
"Mndt": {
"MndtId": "NL-B2B60",
"LclInstrm": "B2B",
"Ocrncs": {
"SeqTp": "RCUR",
"Frqcy": "ADHO",
"Drtn": {
"FrDt": "2021-04-09"
}
},
"MaxAmt": "1000",
"CdtrSchmeId": "BE81ZZZ1234567891",
"Cdtr": {
"Nm": "Merchant Company",
"PstlAdr": {
"AdrLine": "Companystreet 100",
"PstCd": "1000",
"TwnNm": "Brussel",
"Ctry": "BE"
},
"Id": "BE123456789",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "merchant.email@example.com"
}
},
"Dbtr": {
"Nm": "Twikey BV",
"PstlAdr": {
"AdrLine": "Bisschop de Vetplein 7",
"PstCd": "5126CA",
"TwnNm": "Gilze",
"Ctry": "NL"
},
"Id": "65772989",
"CtryOfRes": "NL",
"CtctDtls": {
"EmailAdr": "support@twikey.com",
"Othr": "custNumber002"
}
},
"DbtrAcct": "BE31798258923546",
"DbtrAgt": {
"FinInstnId": {
"BICFI": "GKCCBEBB",
"Nm": "BELFIUS BANK"
}
},
"RfrdDoc": "General terms and conditions",
"SplmtryData": [
{
"Key": "SignerMethod#0",
"Value": "mock"
},
{
"Key": "Signer#0",
"Value": "Twikey Mock Signer"
},
{
"Key": "SignerPlace#0",
"Value": "Ghent"
},
{
"Key": "SignerDate#0",
"Value": "2021-04-09T12:49:42Z"
}
]
},
"EvtTime": "2021-04-09T13:19:34Z"
},
{
"AmdmntRsn": {
"Orgtr": {
"Nm": "Twikey NV",
"PstlAdr": {
"AdrLine": "Stationstraat 43",
"PstCd": "9000",
"TwnNm": "Gent",
"Ctry": "BE"
},
"Id": "BE0533800797",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "support@twikey.com"
}
},
"Rsn": "_T50"
},
"Mndt": {
"MndtId": "CF2498",
"LclInstrm": "CORE",
"Ocrncs": {
"SeqTp": "RCUR",
"Frqcy": "ADHO",
"Drtn": {
"FrDt": "2021-04-09"
}
},
"CdtrSchmeId": "BE81ZZZ1234567891",
"Cdtr": {
"Nm": "Merchant Company",
"PstlAdr": {
"AdrLine": "Companystreet 100",
"PstCd": "1000",
"TwnNm": "Brussel",
"Ctry": "BE"
},
"Id": "BE123456789",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "merchant.email@example.com"
}
},
"Dbtr": {
"Nm": "Company NV",
"PstlAdr": {
"AdrLine": "streetname 100",
"PstCd": "1000",
"TwnNm": "Brussel",
"Ctry": "BE"
},
"Id": "BE0154521254",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "customer.email@example.com",
"Othr": "custNumber00125"
}
},
"DbtrAcct": "BE12123356223353",
"DbtrAgt": {
"FinInstnId": {
"BICFI": "GKCCBEBB",
"Nm": "BELFIUS BANK"
}
},
"RfrdDoc": "HvvGeWz627a",
"SplmtryData": [
{
"Key": "SignerMethod#0",
"Value": "print"
},
{
"Key": "Signer#0",
"Value": "customer.email@example.com"
},
{
"Key": "SignerPlace#0",
"Value": "(Imported)"
},
{
"Key": "SignerDate#0",
"Value": "2021-04-09T10:03:44Z"
}
]
},
"OrgnlMndtId": "CF2498",
"CdtrSchmeId": "BE81ZZZ1234567891",
"EvtTime": "2021-04-09T13:26:53Z"
},
{
"CxlRsn": {
"Orgtr": {
"Nm": "Twikey NV",
"PstlAdr": {
"AdrLine": "Stationstraat 43",
"PstCd": "9000",
"TwnNm": "Gent",
"Ctry": "BE"
},
"Id": "BE0533800797",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "support@twikey.com"
}
},
"Rsn": "Custom cancel reason"
},
"OrgnlMndtId": "CF2506",
"CdtrSchmeId": "BE81ZZZ1234567891",
"EvtTime": "2021-04-09T13:30:51Z"
}
]
}
HTTP Request
GET https://api.twikey.com/creditor/mandate
Headers
By default the mandate feed returns CORE,B2B and CREDITCARD, however if you require other types you can include them by adding the extra types.
Query Parameters
To reduce the feed and only return information you need, you can opt to use the include parameters.
Example: GET /creditor/mandate?include=mandate&include=signature
Name | Returned parameters |
---|---|
include=mandate | TemplateId, MandateName, custom attributes |
include=person | FirstName, LastName, Language |
include=signature | SignerMethod#0, Signer#0, SignerPlace#0, SignerDate#0 |
include=plan | Plan |
include=tracker | TRACKER (shortcode used for invite) |
include=seq | EvtId (returned in the Mndt object) |
include=cancelled_mandate | returns additional customer information on a cancel feed (email and customerNumber) |
include=paidamount | return the amount paid when signing the mandate (returned in the splmtryData object) |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error Codes
Code | Description |
---|---|
err_call_in_progress | Request already in progress by another client |
Read the high level structure document for a more in depth explanation.
Mandate query
Create a search query to return all contracts for a specific iban, customer or a combination of different query parameters.
The response is limited to a set of 500 contracts per page. The response returns the contracts found based on your search query.
curl https://api.twikey.com/creditor/mandate/query?iban=BE21798857497403 \
-H 'authorization: **authorization**' \
$host = "https://api.twikey.com";
$ct = **ct_id**;$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate/query?iban=BE21798857497403");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/mandate/query?iban=BE21798857497403',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void updateFeed(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/mandate/query?iban=BE21798857497403")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void updateFeed(){
RestClient client = new RestClient(host + "/creditor/mandate/query?iban=BE21798857497403");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Contracts": [
{
"id": 3291639,
"type": "CORE",
"state": "SIGNED",
"suspended": false,
"pdfAvailable": true,
"mandateNumber": "MNDT123",
"contractNumber": "General Terms",
"ct": 2223,
"signDate": "2024-03-01T15:02:45Z",
"iban": "BE21798857497403",
"bic": "GKCCBEBB",
"attributes": {
"vehicle": null,
"km": null
}
},
{
"id": 3291538,
"type": "CORE",
"state": "SIGNED",
"suspended": true,
"pdfAvailable": true,
"mandateNumber": "MNDT456",
"contractNumber": "General Terms",
"ct": 2223,
"signDate": "2024-03-01T13:44:39Z",
"iban": "BE21798857497403",
"bic": "GKCCBEBB",
"attributes": {
"vehicle": "Sedan",
"km": "12000"
}
}
],
"_links": {
"self": "/creditor/mandate/query?page=0"
}
}
HTTP Request
GET https://api.twikey.com/creditor/mandate/query?parameter=value
Query Parameters
At least one of iban
, customerNumber
or email
is required. state
is optional. A combination of these parameters is possible.
Name | Description | Required | Type |
---|---|---|---|
iban | The IBAN of the contract | Yes | string |
customerNumber | The customer number | Yes | string |
The email address of the customer | Yes | strng | |
state | optional, return only contracts in a specific state (SIGNED by default. Value in uppercase) | No | string |
page | Pagination is returned at the end of the response, include 'page' with a page number to go to the next or previous set of results | No | number |
- customerNumber is prioritized over email when you combine both.
- state: possible values are
SIGNED
,PREPARED
orCANCELLED
.
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) |
429 | Too many requests |
Error codes
Code | Description |
---|---|
err_not_found | No contracts found with the given parameter(s) value |
err_missing_params | Missing parameters or incorrect parameter value passed. |
Cancel agreements
Agreements 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. Our advise is to communicate cancellations to Twikey. That way all databases are up to date and in some cases the cancellation is also forwarded to the debtor bank. The creditor, the creditor bank or the debtor bank, can initiate this request. Afterwards the update is distributed to all parties.
curl -X DELETE https://api.twikey.com/creditor/mandate?mndtId123&rsn=test \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$ct = **ct_id**;$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate".
"?mndtId=mndtId123&rsn=some%20reason"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
curl_setOpt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/mandate?mndtId=**mdntId**&rsn=some%20reason',
method: 'DELETE',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void cancelMandate(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/mandate?mndtId=**mndtId&rsn=some%20reason")
.delete(null)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void cancelMandate(){
RestClient client = new RestClient(host + "/creditor/mandate" +
"?mndtId=mndtId123" +"
&rsn=some%20reason"
);
RestRequest request = new RestRequest(Method.DELETE);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
DELETE https://api.twikey.com/creditor/mandate
Query Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | 35 |
rsn | Reason of cancellation (Can be R-Message) | Yes | string | 200 |
notify | Notify the customer by email when true | No | boolean |
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 mndtId was provided or invalid mndtId supplied |
err_provide_reason | Please provide reason |
err_invalid_state | Contract state does not allow a cancel (eg. already cancelled) |
err_invalid_cancel_forbidden | Due to the mandate cancellation strategy of the profile, open transactions exist for this mandate |
Fetch mandate details
Retrieve details of a specific mandate. Since the structure of the mandate is the same as in the update feed but doesn't include details about state, 2 extra headers are added. Though this is perfect for one-offs, for updates we recommend using the feed.
curl https://api.twikey.com/creditor/mandate/detail?mndtId=mndtId123 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate/detail" . "
?mndtId=mndtId123"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
curl_setOpt($ch, CURLOPT_CUSTOMREQUEST, "GET");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/mandate/detail?mndtId=mndtId123',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void getMandateDetail(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.get()
.url(host +
"/creditor/mandate/detail" +
"?mndtId=mndtId123"
)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void getMandateDetail(){
RestClient client = new RestClient(host +
"/creditor/mandate/detail" +
"?mndtId=mndtId123"
);
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Mndt": {
"MndtId": "MNDT8380",
"LclInstrm": "CORE",
"Ocrncs": {
"SeqTp": "RCUR",
"Frqcy": "ADHO",
"Drtn": {
"FrDt": "2022-09-28"
}
},
"CdtrSchmeId": "BE81ZZZ1234567891",
"Cdtr": {
"Nm": "Merchant Company",
"PstlAdr": {
"AdrLine": "Companystreet 51N",
"PstCd": "9000",
"TwnNm": "Gent",
"Ctry": "BE"
},
"Id": "BE0123456789",
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "merchant.email@example.com"
}
},
"Dbtr": {
"Nm": "Luke Devon",
"PstlAdr": {
"AdrLine": "Stationstraat 43",
"PstCd": "9051",
"TwnNm": "Gent",
"Ctry": "BE"
},
"CtryOfRes": "BE",
"CtctDtls": {
"EmailAdr": "debtor@example.com",
"Othr": "custNumber001"
}
},
"DbtrAcct": "BE31798258915655",
"DbtrAgt": {
"FinInstnId": {
"BICFI": "GKCCBEBB",
"Nm": "BELFIUS BANK"
}
},
"RfrdDoc": "Terms and Conditions",
"SplmtryData": [
{
"Key": "TRACKER",
"Value": "rWFq"
},
{
"Key": "MandateName",
"Value": "Core Mandate"
},
{
"Key": "TemplateId",
"Value": 2223
},
{
"Key": "amount",
"Value": "10"
},
{
"Key": "licenseplate",
"Value": "1-AAA-001"
},
{
"Key": "plan",
"Value": null
},
{
"Key": "FirstName",
"Value": "Luke"
},
{
"Key": "LastName",
"Value": "Devon"
},
{
"Key": "Language",
"Value": "nl"
},
{
"Key": "SignerMethod#0",
"Value": "print"
},
{
"Key": "Signer#0",
"Value": "Luke Devon"
},
{
"Key": "SignerPlace#0",
"Value": "(Imported)"
},
{
"Key": "SignerDate#0",
"Value": "2022-09-28T13:42:14Z"
}
]
}
}
HTTP Request
GET https://api.twikey.com/creditor/mandate/detail
Query Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | 35 |
force | Also include non-signed states | No | boolean |
Response
Code | Description |
---|---|
200 | Details of the mandate (see /creditor/mandate) |
400 | User error if parameter is given but not valid (available in apierror header and response) |
429 | Too many requests |
Header Response
Header | Description |
---|---|
X-STATE | State of the mandate |
X-COLLECTABLE | Whether this mandate can be used for collections (true/false) |
Possible states
State | Description |
---|---|
PREPARED | User created the mandate, but the mandate is not signed yet |
SIGNED | Client signed the mandate |
EXPIRED | Mandate is beyond it's final date |
CANCELLED | Mandate has been revoked |
SIGNED_PENDING_DEBTOR_BANK | only in case of B2B - debtor bank needs to validate the mandate |
REFUSED_BY_DEBTOR_BANK | only in case of B2B - signed but debtor bank refused the mandate |
REQUIRES_MORE_SIGNATURES | mandate needs to be signed by a 2nd person - option needs to be activated on template level |
only in case of B2B - mandate has been printed by debtor | |
PENDING_MERCHANT_APPROVAL | mandate has been uploaded by debtor but needs to be validated by merchant |
Possible events
Event | Description |
---|---|
Cancel | depending on the state of a mandate |
Accepted Bank | depending on the state of a mandate |
Reject Bank | depending on the state of a mandate |
Sign | depending on the state of a mandate |
depending on the state of a mandate | |
Uploaded | depending on the state of a mandate |
Debtor Upload | depending on the state of a mandate |
Expired | depending on the state of a mandate |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was selected or invalid mndtId supplied |
err_no_contract | No contract was found |
err_invalid_state | Contract was not signed (ignored when force=true) |
Update mandate details
You can change details of a mandate. Note: Include email only when changing it's value. bic code is generated automatically based on iban if not included. Custom attributes that are defined in the template can also be updated.
Note: Company name and language are always updated on the mandate itself, not the owner. Note: Mobile number and email are always updated on both the mandate and customer.
curl POST 'https://api.twikey.com/creditor/mandate/update' \
--h 'Authorization: ....' \
--d 'mndtId=MDT123' \
--d 'address=Stationstraat 43' \
--d 'zip=9000' \
--d 'city=Gent' \
--d 'country=BE' \
--d 'mobile=+32 483 115862' \
--d 'iban=BE75 0509 9307 0051' \
--d 'bic=BRUBBEB'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/mandate/update',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'mndtId=MDT123&address=Stationstraat%2043&zip=9000&city=Gent&country=BE&iban=BE32%201234%201234%201234&bic=BRUBBEB',
CURLOPT_HTTPHEADER => array(
'Authorization: ....'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
myHeaders.append("Authorization", "....");
var urlencoded = new URLSearchParams();
urlencoded.append("mndtId", "MDT123");
urlencoded.append("address", "Stationstraat 43");
urlencoded.append("zip", "9000");
urlencoded.append("city", "Gent");
urlencoded.append("country", "BE");
urlencoded.append("iban", "BE32 1234 1234 1234");
urlencoded.append("bic", "BRUBBEB");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/mandate/update", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "mndtId=MDT123&address=Stationstraat 43&zip=9000&city=Gent&country=BE&iban=BE32 1234 1234 1234&bic=BRUBBEB");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/mandate/update")
.method("POST", body)
.addHeader("Authorization", "....")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/mandate/update");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "....");
request.AddParameter("mndtId", "MDT123");
request.AddParameter("address", "Stationstraat 43");
request.AddParameter("zip", "9000");
request.AddParameter("city", "Gent");
request.AddParameter("country", "BE");
request.AddParameter("iban", "BE32 1234 1234 1234");
request.AddParameter("bic", "BRUBBEB");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
HTTP Request
POST https://api.twikey.com/creditor/mandate/update
Query Parameters
B2B type mandate: iban cannot be updated once the document is signed.
Name | Description | Required | Type | Max. |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | 35 |
ct | Move the document to a different template ID (of the same type) | No | number | |
state | active or passive (activated or suspend mandate) | No | String | 7 |
mobile | Customer's mobile number to avoid errors use E.164 format | No | String | 50 |
iban | Debtor's IBAN | No | String | 35 |
bic | Debtor's BIC code | No | String | 11 |
email address of debtor | No | String | 70 | |
firstname | Firstname of the debtor | No | String | 50 |
lastname | Lastname of the debtor | No | String | 50 |
companyName | Company name on the mandate | No | String | 140 |
vatno | The enterprise number (can only be changed if companyName is changed) | No | String | 50 |
customerNumber | The customer number (can be added, updated or used to move a mandate) | No | string | 50 |
l | language on the mandate | No | String | 2 |
To update the address all fields below are required | Yes | - | - | |
address | Address (street + number) | No | String | 70 |
city | City of debtor | No | String | 50 |
zip | Zipcode of debtor | No | String | 50 |
country | ISO format | No | String | 2 |
HTTP Response
Code | Description |
---|---|
204 | The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was found |
err_invalid_country | Invalid country code or not in ISO format (2 letters) |
err_invalid_state | mandate state is invalid: some query parameters are invalid or not allowed in the current state. |
Move a mandate
It is possible to move the mandate to another customer (owner). Include the customer number of the target in the request.
- The email address is updated on the mandate using the new owner's one.
- The mobile number is updated on the mandate using the new owner's one.
- Address and name are preserved on the mandate.
- payment links and invoices remain stored on the original owner. This can be done for both pending and signed mandates.
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.
curl -X POST https://api.twikey.com/creditor/customeraccess \
-H 'authorization: **authorization**' \
-d 'mndtId=mndtId123'
$host = "https://api.twikey.com";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/customeraccess");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
curl_setOpt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setOpt($ch, CURLOPT_POSTFIELDS, array(
"mndtId" => "mdntId123"
);
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
?>
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/customeraccess',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body:{
'mndtId': 'mndtId123'
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void updateMandate(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("mndtId", "mndtId123")
.build();
Request request = new Request.Builder()
.post(body)
.url(host + "/creditor/customeraccess")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void updateMandate(){
RestClient client = new RestClient(host + "/creditor/customeraccess);
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("authorization", authorisation);
request.AddParameter("application/x-www-form-urlencoded",
"mndtId=mndtId123" ,
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"token": "A4DE98FD091....",
"url": "https://merchant.twikey.com/p/customeraccess?token=A4DE98FD0915F"
}
HTTP Request
POST https://api.twikey.com/creditor/customeraccess
Query Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | 35 |
HTTP Response
Code | Description |
---|---|
200 | Request succeeded |
400 | Bad Request |
Error Codes
Name | Description |
---|---|
err_no_contract | No contract was found |
Retrieve pdf
Retrieve pdf of a mandate
curl https://api.twikey.com/creditor/mandate/pdf?mndtId123 \
-H 'authorization: **authorization**'
# Example for downloading the content straight to a file
curl -v -X GET https://api.twikey.com/creditor/mandate/pdf?mndtId=mndtId123 -H 'authorization: **authorization**' --output test.pdf
$host = "https://api.twikey.com";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate/pdf?mndtId=mndtId123");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
curl_setOpt($ch, CURLOPT_CUSTOMREQUEST, "GET");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/mandate/pdf?mndtId=mdntId123',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void retrievePdf(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.get()
.url(host + "/creditor/mandate/pdf?mndtId=mndtId123")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void retrievePdf(){
RestClient client = new RestClient(
host +
"/creditor/mandate/pdf" +
"?mndtId=mndtId123"
);
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
request.AddParameter("application/x-www-form-urlencoded",
"mandate.pdf",
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
GET https://api.twikey.com/creditor/mandate/pdf
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
mndtId | Mandate Reference | Yes | string |
HTTP Response
Code | Description |
---|---|
200 | PDF is available in the body of the response |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was found |
Upload pdf
Import existing mandate pdf (eg. scan of a printed document) After import the mandate is set on signed state.
curl -X POST https://api.twikey.com/creditor/mandate/pdf \
-H 'authorization: **authorization**' \
-d 'mndtId=mndtId123' \
-d @mndtId123.pdf
$host = "https://api.twikey.com";
$post = array(
"file_box"=>"@/path/to/mndtId123.pdf",
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/mandate/pdf?mndtId=mndtId123");
curl_setOpt($ch, CURLOPT_HTTPHEADER, array(
"authorization: $authorization",
"Content-Type: application/pdf',
"Content-Length: ' . strlen($post))
);
curl_setOpt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setOpt($ch, CURLOPT_POSTFIELDS, $post);
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null; //collected through login
var requestUrl = 'path/to/import.json';
var request = new XMLHttpRequest();
request.open('GET', requestUrl);
request.responseType = 'json';
request.send();
request.onload = function (){
var options = {
host: host,
port: '443',
path: '/creditor/mandate/pdf?mndtId=mndtId123',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
data: request.response
};
var req = https.request(options, function (res) {
console.log(res);
});
};
import java.io.FileReader;
import java.io.BufferedReader;
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void uploadPdf(){
String pdf;
try{
FileReader fr = new FileReader("/path/to/mndtId123.pdf");
BufferedReader br = new BufferedReader(fr);
String s;
while((s = br.readLine()) != null) {
pdf+=s;
}
fr.close();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.post(pdf)
.url(host + "/creditor/mandate/pdf?mndtId=mndtId123")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
} catch (FileNotFoundException e){
System.out.printLn(e);
}
}
}
using System.IO;
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
String pdf = file.ReadAllText("\path\to\mndtid123.pdf");
public void uploadPdf(){
RestClient client = new RestClient(
host +
"/creditor/mandate/pdf" +
"?mndtId=mndtId123"
);
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("authorization", authorisation);
request.AddParameter("application/x-www-form-urlencoded",
pdf,
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
POST https://api.twikey.com/creditor/mandate/pdf
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
mndtId | Mandate Reference | Yes | string |
HTTP Response
Code | Description |
---|---|
200 | Import of the pdf was done |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_contract | Contract not found |
err_invalid_iban | no (valid) iban on the mandate |
Retrieve legal terms
Retrieve legal terms
curl https://api.twikey.com/creditor/legal?locale=nl_BE \
-H 'authorization: authorization'
$host = "https://api.twikey.com";
$authorisation = null; // collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/legal?locale=nl_BE");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
host = "api.twikey.com",
authorization = null, // collected through login
options = {
host: host,
port: '443',
path: '/creditor/legal?locale=nl_BE',
headers: {
'Content-Type': 'application/json'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; // collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
Request request = new Request.Builder()
.url(host + "/creditor/legal?locale=nl_BE")
.addHeader("content-type", "application/json")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/legal?locale=nl_BE");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/json");
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"tcUrl": "https://www.beta.twikey.com/nl/tc.html",
"bySigning": "Door ondertekening van dit mandaatformulier ...",
"rightsCore": "U kunt een Europese domiciliëring laten terugbetalen. Vraag ...",
"rightsB2b": "Dit mandaat is uitsluitend bedoeld voor betalingen tussen bedrijven. U hebt ...",
"infoCorrect": ""
}
Parameters
Name | Description | Required | Type |
---|---|---|---|
locale | the locale (fr, fr_FR, fr_BE, de, nl, nl_BE, nl_NL, es, pt, it) | no (defaults to en) | string |
Responses
Code | Description |
---|---|
200 | The request has succeeded |
Subscriptions
Add a subscription
A subscription (previously called plan) can be added on an agreement. This means than when the subscription is run a new transaction will be created using the defined schedule. If you opted for the automatic sending this will be collected as soon as the bank permits it.
request without and with a plan:
curl -X POST https://api.twikey.com/creditor/subscription \
-H 'authorization: **authorization**' \
-d 'mndtId=TEST03'\
-d 'msg=Monthly subscription'\
-d 'ref=MyRef'\
-d 'amount=12.00'\
-d 'recurrence=1m'\
-d 'start=2022-11-29'
curl -X POST https://api.twikey.com/creditor/subscription \
-H 'authorization: **authorization**' \
-d 'mndtId=TEST03'\
-d 'ref=MyRef'\
-d 'plan=myplan'\
-d 'start=2022-11-29'
HTTP Request
POST https://api.twikey.com/creditor/subscription
{
"id": 10,
"state": "active",
"amount": 12.0,
"message": "Monthly subscription",
"ref": "MyRef",
"plan": 0,
"runs": 0,
"stopAfter": 5,
"start": "2022-11-29",
"next": "2022-12-01",
"recurrence": "1m",
"mndtId": "TEST03"
}
Request Headers
Name | Description | type | max. length |
---|---|---|---|
Idempotency-Key | Unique key usable only once per request every 24hrs | string | 64 |
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
mndtId | Mandate Reference | Yes | string |
message | Message to the subscriber | Yes | string |
plan | Name of the base plan | No | string |
ref | Reference of the subscription (important for updates) [*1] | No | string |
amount | Amount of the transaction | Yes | number |
stopAfter | Number of times to execute | No | number |
recurrence | 1w / 1m / 2m / 3m / 4m / 6m /12m (default 1m) | No | string |
start | Start of subscription eg. 2022-11-01 (future date only) | Yes | date |
[*1]: The reference is converted to uppercase and can't contain any spaces.
HTTP Response
Code | Description |
---|---|
200 | The server has fulfilled the request |
400 | User error if parameter is given but not valid (available in apierror header and response) |
409 | Conflict. Idempotency-key already used |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was found |
err_invalid_date | Invalid start date |
err_plan_range | Invalid stopAfter value |
err_msg_missing | Message not supplied |
err_invalid_amount | Invalid amount |
err_invalid_params | Some parameter is invalid, see the extra in the response body for more information. |
err_duplicate_ref | Idempotency-key was already used in the last 24hrs. |
Update a subscription
Sometimes a subscription needs to be updated. This endpoint allows the update by using the previously passed reference for a specific agreement.
The update subscription and patch subscription are similar requests, the difference be that with the post you can replace a subscription (cancel current and start new) the patch can't replace a subscription.
curl -X POST https://api.twikey.com/creditor/subscription/:agreement/:ref \
-H 'authorization: **authorization**' \
-d 'message=mymessage'\
-d 'plan=planName'\
-d 'amount=10.22'
HTTP Request
POST https://api.twikey.com/creditor/subscription/:agreement/:ref
:agreement
: the mandate reference (e.g.: MNDT123):ref
: the reference of the subscription
{
"id": 10,
"state": "active",
"amount": 10.22,
"message": "mymessage",
"ref": "reference123",
"plan": 0,
"runs": 0,
"stopAfter": 5,
"start": "2022-11-29",
"next": "2022-12-01",
"recurrence": "1m",
"mndtId": "TEST03"
}
Query Parameters
Name | Description | Required | Type | |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | |
message | Message to the subscriber | Yes | string | |
amount | Amount of the transaction | Yes | number | |
start | Start date of the subscription (yyyy-mm-dd). This is also the first execution date. Only a future date is accepted | Yes | date | |
plan | Name of the base plan **[1]* | No | string | |
recurrence | 1w / 1m / 2m / 3m / 4m/ 6m /12m (default 1m) | No | string | |
stopAfter | Number of times to execute **[2]* | No | number |
**[*1]**: When passing a plan the values of message, amount and recurrence are ignored if passed during the request.
**[*2]**: Previous executions (runs) are not taken into account for the new subscription. It starts from 0 runs.
HTTP Response
Code | Description |
---|---|
204 | The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was found |
err_invalid_date | Invalid start date |
err_plan_range | Invalid stopAfter value |
err_msg_missing | Message not supplied |
err_invalid_amount | Invalid amount |
Patch a subscription
To update a subscription but not replace it, you can use this endpoint. It allows you to update specific fields or move the subscription to a different mandate.
curl -X PATCH https://api.twikey.com/creditor/subscription/MNDT100/myreference
?mndtId=MNDT200&message=mymessage&amount=25
-H 'authorization: **authorization**'
HTTP Request
PATCH https://api.twikey.com/creditor/subscription/:agreement/:ref{queryparameters}
:agreement
: the mandate reference (e.g.: MNDT123):ref
: the reference of the subscription
{
"id": 10,
"state": "active",
"amount": 25.0,
"message": "mymessage",
"ref": "myreference",
"plan": 0,
"runs": 0,
"stopAfter": 5,
"start": "2024-11-29",
"next": "2024-12-01",
"recurrence": "1m",
"mndtId": "MNDT200"
}
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
mndtId | Move the subscription to a different mandate | No | string |
message | Message to the subscriber | No | string |
amount | Amount of the transaction | No | number |
HTTP Response
Code | Description |
---|---|
200 | The server has fulfilled the request |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_contract | No contract was found |
err_invalid_amount | Invalid amount |
Cancel a subscription
A subscription can be cancelled by using it's ref for a specific agreement.
curl -X DELETE https://api.twikey.com/creditor/subscription/:agreement/:ref \
-H 'authorization: **authorization**'
HTTP Request
DELETE https://api.twikey.com/creditor/subscription/:agreement/:ref
HTTP Response
Code | Description |
---|---|
204 | The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_not_found | No subscription found (agreement, ref or combination incorrect) |
Retrieve a single subscription
A single subscription can be fetched for a specific agreement, but note that this call is rate limited.
curl https://api.twikey.com/creditor/subscription/:agreement/:ref \
-H 'authorization: **authorization**'
HTTP Request
GET https://api.twikey.com/creditor/subscription/:agreement/:ref
{
"id": 10,
"state": "active",
"amount": 12.0,
"message": "Monthly subscription",
"ref": "MyRef",
"plan": 0,
"runs": 0,
"stopAfter": 5,
"start": "2022-11-29",
"last": null,
"next": "2022-12-01",
"recurrence": "1m",
"mndtId": "TEST03"
}
HTTP Response
Code | Description |
---|---|
200 | The server has fulfilled the request |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_not_found | No subscription found (agreement, ref or combination incorrect) |
Query all subscription
All subscription can be retrieved based on some criteria.
curl -X POST https://api.twikey.com/creditor/subscription/query \
-H 'authorization: **authorization**' \
-d 'mndtId=mdntId123'\
-d 'customerNumber=123'\
-d 'state=active'
{
"Subscriptions": [
{
"id": 10,
"state": "active",
"amount": 12.0,
"message": "Message for customer",
"ref": "MyRef",
"plan": 0,
"runs": 0,
"stopAfter": 5,
"start": "2022-10-29",
"last": "2022-11-29",
"next": "2022-12-01",
"recurrence": "1m",
"mndtId": "PLOPSAABO3"
},
...
]
}
HTTP Request
GET https://api.twikey.com/creditor/subscription/query
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
mndtId | Mandate Reference | No | string |
customerNumber | Subscriptions by customerNumber | No | string |
state | State of the subscription (active, suspended, cancelled, closed) | No | string |
page | Page of the results (if more than 1 is available) | No | number |
HTTP Response
Code | Description |
---|---|
200 | The server has fulfilled the request |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
No specific error codes. When passing incorrect values the response will return an empty subscriptions array.
Action a subscription
A subscription can be suspended/resumed by using it's ref for a specific agreement.
curl -X POST https://api.twikey.com/creditor/subscription/:agreement/:ref/:action \
-H 'authorization: **authorization**'
HTTP Request
POST https://api.twikey.com/creditor/subscription/:agreement/:ref/:action
Where action is either 'suspend' or 'resume'
HTTP Response
Code | Description |
---|---|
204 | The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_not_found | No subscription found (agreement, ref or combination incorrect) |
err_invalid_params | Invalid parameter |
Transactions
New transaction
Add transaction to an existing mandate. This transaction will be sent to the bank when the collection is sent to the bank either automatically or manually.
HTTP Request
POST /creditor/transaction
curl -X POST https://api.twikey.com/creditor/transaction \
-H 'authorization: **authorization**'\
-d 'mndtId=mndtId123' \
-d 'message=Monthly payment' \
-d 'amount=10'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"mndtId=mndtId123"
."&message=Monthly payment"
."&amount=10");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body :{
'mndtId' : 'mndtId123',
'message': 'Monthly payment',
'amount': '10'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void addTransaction(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("mndtId", "mndtId123")
.add("message", "Monthly payment")
.add("amount", "10")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transaction")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; //collected through logIn
public void addTransaction(){
RestClient client = new RestClient(host + "/creditor/transaction");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"mndtId=mndtId123" +
"message=Monthly payment" +
"amount=10"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Entries": [
{
"id": 381563,
"contractId": 325638,
"mndtId": "MNDT123",
"contract": "Algemene voorwaarden",
"amount": 10.0,
"msg": "Monthly payment",
"place": null,
"ref": null,
"date": "2017-09-16T14:32:05Z"
}
]
}
Request Headers
Name | Description | type | max. length |
---|---|---|---|
Idempotency-Key | Unique key usable only once per request every 24hrs | string | 64 |
Request Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
mndtId | Mandate Reference | Yes | string | 35 |
date | Date of the transaction or now when empty | No | date | |
reqcolldt | Requested collection date of the transaction or null to collect as soon as possible | No | date | |
message | Message to the customer [*1] | Yes | string | |
ref | Your reference | No | string | |
amount | Amount to be billed | Yes | decimal | |
place | Optional place | No | string | |
refase2e | The reference is used as E2E identifier for the first payment Conform with the Rule Book of the EPC. |
No | boolean |
[*1]:
- This is the message that both you and your customer will see on their bank statement. More info..
- Belgium: The message is treated as structured message (OGM) when it is 12-characters long and the check-sum is correct.
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) |
409 | Conflict. Idempotency-key already used |
Error codes
Code | Description |
---|---|
err_no_contract | No mandate found |
err_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 |
err_invalid_params | Some parameter is invalid, see the extra in the response body for more information. |
err_duplicate_ref | Idempotency-key was already used in the last 24hrs. |
Transaction feed
Retrieve list of transactions that had changes since the last call. This endpoint allows to retrieve a list of transactions for which new payment information has been received since the last call. This endpoint doesn't require any parameters. If we receive feedback from the bank, we mark the transaction with status "error" or "paid".
The final flag is important as it indicates whether or not there are still automatic actions going on. 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 where no more automatic actions can be performed. Here an action will be required on your part. False means that we still have actions pending to debit the debtor's account. Note that a paid state paid with final flag on true can be reverted by the bank (refund request by the debtor).
curl https://api.twikey.com/creditor/transaction \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction");
curl_setopt($ch, CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization');
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void getTransactions(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transaction")
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void getTransactions(){
RestClient client = new RestClient(host + "/creditor/transaction");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Entries": [
{
"id": 123456789,
"contractId": 10001,
"mndtId": "mandateReference",
"contract": "contractNumber",
"amount": 99.99,
"msg": "transaction message",
"place": "web",
"ref": null,
"final": true,
"state": "PAID",
"bkdate": "2017-03-21T08:13:21Z",
"reqcolldt": "2017-03-23T08:13:21Z"
},
{
"id": 987654321,
"contractId": 2002,
"mndtId": "mandateReference",
"contract": "contractNumber",
"amount": 100,
"admincharge": 10.0,
"msg": "transaction message",
"place": "web",
"ref": "INVOICE001",
"final": false,
"state": "ERROR",
"bkerror": "MS03",
"bkmsg": "Geen reden opgegeven",
"bkdate": "2016-09-21T08:13:21Z",
"reqcolldt": null
},
{
"id": 56789123,
"contractId": 3003,
"mndtId": "mandateReference",
"contract": "contractNumber",
"amount": 49.99,
"msg": "buy item X",
"place": "web",
"ref": null,
"final": true,
"state": "ERROR",
"bkerror": "AC04",
"bkmsg": "Rekening afgesloten",
"bkdate": "2016-08-29T13:14:40Z",
"reqcolldt": null
},
"..."
]
}
Including all parameters:
{
"Entries": [
{
"id": 218914,
"contractId": 1320345,
"mndtId": "MNDT123",
"contract": "CTR123",
"amount": 5.63,
"admincharge": 10.0,
"msg": "Delivery fee",
"place": null,
"ref": "DLVRY EXPRESS 001",
"date": "2020-12-09T16:07:19Z",
"final": true,
"state": "ERROR",
"bkerror": "AM04",
"bkmsg": "Insufficient funds",
"bkdate": "2020-12-09",
"lastupdate": "2020-12-09T16:07:24Z",
"bkamount": 0,
"collection": 9314,
"reqcolldt": "2020-12-11",
"link": "https://mycompany.twikey.com/payment/tr_...",
"actions": [
{
"type": "FAIL_SOFT",
"reason": "AM04",
"action": "again",
"at": "2020-12-09T16:07:24Z"
},
{
"type": "FAIL_SOFT",
"reason": "AM04",
"action": "backup",
"at": "2020-12-10T03:30:09Z"
}
]
}
]
}
HTTP Request
GET /creditor/transaction
Headers
Header name | Description |
---|---|
X-RESUME-AFTER | Resume the feed after a specific sequence id (requires the parameter include=seq ) |
include parameter
The include parameter (optional) can be used several times with a different value
to include additional information about the transaction in the response.
Example: GET /creditor/transaction?include=collection&include=lastupdate&include=action&include=link
Value | Description |
---|---|
collection | returns the batch id |
lastupdate | returns the last update in datetime format and 'bkdate' as date. |
action | returns all the dunning steps taken for a transaction |
action_payment | returns only dunning steps taken since the last time the feed was read. If you both include action and action_payment then the action parameter is ignored |
link | returns the url of the dunning payment link (when in dunning) |
stage | returns the the current final stage of the transaction |
seq | return a sequence ID in the feed |
Dunning types
- FAIL_SOFT: Action(s) when insufficient funds
- FAIL_HARD: Action(s) when customer refuses
- TECHNICAL: Action(s) on failure at bank
Dunning actions
- notify: notification sent to the customer
- backup: alternative payment proposed to the customer
- again: re-offer the transaction.
- state: changed state of the mandate
- wik: official WIK letter sent
- paid: partial payment registered with
amount
as decimal value
Transaction stage values
- NONE: failed and still in dunning.
- UNSETTLED: failed and requires manual action
- WIKLETTER: official WIK letter sent
- AGENCY: sent to the collection agency
- ARCHIVED: planned dunning steps are stopped (those in progress are not)
admincharge
This is returned when a transaction failed and administrative charges were applied.
bkamount
The bkamount is the amount of the transaction was (partially) paid. This value is increased with each partial payment up to a maximum of the total amount.
Possible states
Possible states | Description |
---|---|
PAID | The transaction has been executed + final flag info |
ERROR | The transaction has not been executed + final flag info |
Final flag state
The final flag is only relevant in case of dunning. If a transaction is paid the final state is always true
.
Should the transaction change to failed (eg. chargeback) and dunning is configured the final flag changes to false
as long
as the dunning is running.
Transaction in a state ERROR + final:true
require manual action.
Possible states | Description |
---|---|
TRUE | No more actions are pending. Manual action is required if the state is in error |
FALSE | More actions are pending to debit the debtor's account |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error codes
Code | Description |
---|---|
err_missing_params | No parameter given |
err_no_transaction | No link found based on the transaction |
err_not_found | No link found |
err_call_in_progress | Request already in progress by another client |
Response parameters
Name | Description |
---|---|
date | Date when the transaction was created |
bkdate | Date when the transaction was booked (received feedback from the bank) |
reqcolldt | The requested collection date |
bkerror | Error code when a transaction failed |
bkamount | The amount already paid |
collection | The collection/batch ID in which the transaction was executed |
Transaction status
Retrieve the status of transactions at a certain point in time, this may change at any time. We strongly recommend using the transaction feed to keep your system up to date as you'll never miss an update while this is merely a snapshot in time.
curl https://api.twikey.com/creditor/transaction/detail?id=1234 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction/detail?id=1234");
curl_setopt($ch, CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction/detail?id=1234',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void getTransactionDetail(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transaction/detail?id=1234")
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void getTransactionDetail(){
RestClient client = new RestClient(host + "/creditor/transaction/detail?id=1234");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Entries": [
{
"id": 1234,
"amount": 10.0,
"contract": "Algemene voorwaarden",
"contractId": 325638,
"date": "2017-09-16T14:32:05Z",
"mndtId": "MNDT123",
"msg": "Monthly payment",
"place": null,
"ref": null,
"state": "OPEN",
"reqcolldt": "2021-01-11"
}
]
}
Sample response when using multiple parameters:
{
"Entries": [
{
"id": 558295,
"contractId": 1826643,
"mndtId": "MNDT123",
"contract": "TC",
"amount": 22.0,
"admincharge": 5.0,
"msg": "Monthly payment",
"place": null,
"ref": null,
"date": "2022-03-09T12:56:29Z",
"final": true,
"state": "ERROR",
"bkerror": "AC04",
"bkmsg": "Account closed",
"bkdate": "2022-03-09",
"lastupdate": "2022-03-09T13:30:01Z",
"bkamount": 0,
"collection": 16336,
"link": "https://company.twikey.com/payment/tr_zwBA7H9N5cxbOIw4g5drv2b",
"reqcolldt": "2022-03-11"
}
]
}
HTTP Request
GET /creditor/transaction/detail
Request Parameters
General parameters
Name | Description | Required | Type |
---|---|---|---|
id | id of a transaction | No | string |
ref | ref of a transaction | No | string |
mndtId | mandate reference | No | string |
state parameter
The state parameter (optional) can be used to return only transactions in a specific state. Most common usage of this is in combination with a mandate id, to retrieve all the transactions in a specific state for that mandate.
Example: GET /creditor/transaction/detail?mndtId=MNDT123&state=error
Name | value | Description | Type |
---|---|---|---|
state | OPEN | only return open transactions [*1] | string |
state | PAID | only return paid transactions | string |
state | ERROR | only return transactions in an error state | string |
state | UNPAID | only return transactions in an open/error state | string |
- [*1]: The state is 'OPEN' when:
- When the transaction is not yet sent to the bank. The transaction can still be deleted
- When the transaction is already sent to the bank, but no feedback was received. The transactions can't be deleted
include parameter
The include parameter (optional) can be used several times with a different value
to include additional information about the transaction in the response.
Example: GET /creditor/transaction/detail?id=12345&include=collection&include=lastupdate&include=link
Name | Value | Description |
---|---|---|
include | collection | returns the batch id |
include | lastupdate | returns the last update in datetime format and 'bkdate' as date. |
include | link | returns the url of the dunning payment link (when in dunning) |
Final flag
When a transaction is in a PAID or ERROR state, the final flag is returned in the response.
This can be true or false.
- true: There are no more pending actions. If the transaction is in the state ERROR then a manual action is required.
- false: At least one action is still pending for the transaction.
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) |
429 | Too many requests |
Error codes
Code | Description |
---|---|
err_invalid_params | Either id or ref or mndtId is invalid or is empty |
Action on transaction
Execute an action on a transaction, this will allow you to change the status or start a specific flow for the given transaction.
HTTP Request
POST /creditor/transaction/action
curl -X POST https://api.twikey.com/creditor/transaction/action \
-H 'authorization: **authorization**'\
-d 'id=345' \
-d 'action=reoffer'
$host = "https://api.twikey.com";
$authorization = null; // collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction/action");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"id=345"
."&action=reoffer");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction/action',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body :{
'id' : '345',
'action': 'reoffer'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void addTransaction() {
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("id", "345")
.add("action", "reoffer")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transaction/action")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void addTransaction() {
RestClient client = new RestClient(host + "/creditor/transaction/action");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"id=345" +
'action=reoffer"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Request Parameters
Name | Description | Required | Type | Options |
---|---|---|---|---|
id | Transaction id | Yes | string | |
action | Action to execute for the given transaction | Yes | string | paid, reoffer, backup*, unsettle, archive |
*backup: alternative payment method is sent to the customer by email. This can include a payment link or bank transfer details depending on your configuration.
HTTP Response
Code | Description |
---|---|
204 | The request has succeeded |
201 | Deletion of the transaction has succeeded |
400 | User error if parameter is given but not valid (available in api error header and response) |
Error codes
Code | Description |
---|---|
err_no_transaction | No transaction found |
err_no_contract | No mandate found (or not active) |
err_invalid_params | one of the parameters provided contains invalid data |
Update a transaction
Update parameters for an existing transaction.
HTTP Request
PUT /creditor/transaction
curl -X PUT https://api.twikey.com/creditor/transaction \
-H 'authorization: **authorization**'\
-d 'id=345' \
-d 'message=Monthly payment' \
-d 'amount=10'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS,"id=345"
."&message=Monthly payment"
."&amount=10");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction',
method: 'PUT',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body :{
'id' : '345',
'message': 'Monthly payment',
'amount': '10'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void updateTransaction(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("id", "345")
.add("message", "Monthly payment")
.add("amount", "10")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transaction")
.put(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void updateTransaction(){
RestClient client = new RestClient(host + "/creditor/transaction");
RestRequest request = new RestRequest(Method.PUT);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"id=345" +
"message=Monthly payment" +
'amount=10"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
id | Transaction id | Yes | string |
reqcolldt | Requested date of the billable event | No | string |
message | Message to the customer | No | string |
ref | Your reference | No | string |
amount | Amount to be billed | No | 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 api error header and response) |
Error codes
Code | Description |
---|---|
err_no_transaction | No transaction found |
err_no_contract | No mandate found (or not active) |
err_invalid_params | one of the parameters provided contains invalid data |
err_invalid_date | Invalid Date |
err_invalid_amount | Invalid Amount given |
Refund a transaction
General transactions
If the beneficiary account does not already exist, the account is added to the customer. The IBAN of the refund account is the one registered on the mandate linked to the transaction. After a refund request, the refund batch need to be prepared to be proccessed.
Credit Card transactions
Currently, supported payment providers for refunds are: CCV, Multisafepay, Mollie In the response the id is 1-on-1 with the refund id from your provider.
curl -X POST https://api.twikey.com/creditor/transaction/refund \
-H 'authorization: **authorization**'\
-d 'id=345' \
-d 'iban=BE12356798' \
-d 'bic=BBRUBEBB' \
-d 'message=refund payment' \
-d 'amount=10'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction/refund");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS,"id=345"
."&message=Refund payment"
."&iban=BE12356798"
."&bic=BBRUBEBB"
."&amount=10");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction/refund',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body: {
'id': '345',
'message': 'Refund payment',
'amount': '10',
'iban': 'BE12356798',
'bic': 'BBRUBEBB'
}
};
var req = https.request(options, function (res) {
console.log("result : ", result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void refundTransaction() {
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("id", "345")
.add("message", "Refund payment")
.add("amount", "10")
.add("iban", "BE12356798")
.add("bic", "BBRUBEBB")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transaction/refund")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
}
;
}
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void refundTransaction(){
RestClient client = new RestClient(host + "/creditor/transaction/refund");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"id=345" +
"&message=Refund payment" +
"&amount=10"
"&iban=BE12356798"
"&bic=BBRUBEBB"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
POST /creditor/transaction/refund
Request Headers
Name | Description | type | max. length |
---|---|---|---|
Idempotency-Key | Unique key usable only once per request every 24hrs | string | 64 |
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
id | The transaction id | Yes | String |
message | Message for the refund | Yes | String |
amount | Amount to be refunded | Yes | Number |
ref | Add a reference for the refund | No | String |
place | Place of refund | No | String |
iban | Iban of the Beneficiary account (optional otherwise iban from the mandate) | No | String |
bic | Bic of the Beneficiary account | No | String |
Sample response:
{
"Entries": [
{
"id": "9FD3492820210119162251192138",
"iban": "BE123456789",
"bic": "GKCCBEBB",
"amount": 10.0,
"msg": "Refund payment",
"place": "Ghent",
"ref": "refund reference 123",
"date": "2021-01-19"
}
]
}
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_invalid_params | Missing request parameters or invalid values |
err_msg_missing | message parameter missing |
Remove a transaction
Remove a transaction that wasn't sent to the bank yet based on the id and/or reference.
curl -X DELETE https://api.twikey.com/creditor/transaction \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transaction");
curl_setopt($ch, CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transaction',
method: 'DELETE',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ", result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void removeTransaction() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transaction")
.delete(null)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
}
;
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void removeTransaction(){
RestClient client = new RestClient(host + "/creditor/transaction");
RestRequest request = new RestRequest(Method.DELETE);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
DELETE /creditor/transaction
Request Parameters
At least one parameter must be passed in the request.
Name | Description | Required | Type |
---|---|---|---|
id | a transactionId as returned in the post | No | string |
ref | Transaction reference (ref) as provided in the post to be removed | No | string |
HTTP Response
Code | Description |
---|---|
204 | 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 | Contract not found |
err_not_found | Transaction not found |
err_transaction_invalid_action | Action invalid for the current state of the transaction |
err_duplicate_tx | Multiple transactions with the same reference exist |
Query transactions
Sequentially retrieve all created transactions starting from any given transaction ID. This endpoint is meant only for exporting transactions in bulk
and registering transactions created by subscriptions.
We strongly recommend using the transaction feed to keep your system up to date as you'll never
miss an update while this is merely a snapshot in time.
curl https://api.twikey.com/creditor/transaction/query?fromId=1000000 \
-H 'authorization: **authorization**'
{
"Entries": [
{
"id": 4692115,
"contractId": 3408156,
"ct": 3764,
"subscriptionId": 230863,
"mndtId": "EXAMPLE-AI3I",
"amount": 623.0,
"msg": "Bike",
"place": null,
"ref": "F7B2F7F3910D4B139AE2A3D7C20677E4",
"date": "2024-08-27T08:26:19Z",
"final": true,
"state": "PAID",
"bkdate": "2024-08-23",
"lastupdate": "2024-08-23T08:40:01Z",
"collection": 51014
},
{
"id": 4692116,
"contractId": 3408152,
"ct": 3764,
"subscriptionId": 230858,
"mndtId": "EXAMPLE-AGPWU",
"amount": 849.0,
"msg": "Gloves",
"place": null,
"ref": "079D43896FA44CEC809222C54402B66C",
"date": "2024-08-27T08:26:19Z",
"final": true,
"state": "PAID",
"bkdate": "2024-08-23",
"lastupdate": "2024-08-23T08:40:01Z",
"collection": 51014
},
{
"id": 4692118,
"contractId": 3408186,
"ct": 3764,
"subscriptionId": 230866,
"mndtId": "EXAMPLE-AGPVZ",
"amount": 872.0,
"msg": "Gloves",
"place": null,
"ref": "ACBDD129FBE745838303188F9505DB8E",
"date": "2024-08-27T08:26:19Z",
"final": true,
"state": "PAID",
"bkdate": "2024-08-23",
"lastupdate": "2024-08-23T08:40:02Z",
"collection": 51014
}
],
"_links": {
"self": "/creditor/transaction/query?fromId=4692115",
"next": "/creditor/transaction/query?fromId=4692119"
}
}
HTTP Request
GET /creditor/transaction/query?fromId=1000000
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
fromId | The Id of the transaction from where to start your query | Yes | Number |
mndtId | Mandate Reference | No | string |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | Bad request |
429 | Too many requests |
Error Codes
Code | Description |
---|---|
err_not_found | The ID provided was not found in the environment. The ID provided is included under extra. |
err_missing_params | Some parameter is missing, see the extra in the response body for more information. |
Collections
Execute Collection
Prepare a batch of transactions for collection and sent to collecting agent (Bank integration) defined on the specified template.
HTTP Request
POST /creditor/collect
curl -X POST https://api.twikey.com/creditor/collect \
-d ct=123 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$ct = "**ct_id**";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/collect");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"ct=$ct"
."clltndt=2017-09-15"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
ct = "**ct_id**",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/collect',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"ct":"$ct",
"clltndt":"2017-09-15"
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
private String ct = "**ct_id**";
public void executeCollect(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("ct", ct)
.add("clltndt","2017-09-15")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/collect")
.post(formBody)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void executeCollect(){
RestClient client = new RestClient(host + "/creditor/collect");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"ct=" + ct +
"clltndt="2017-09-15"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
ct | Contract template for which to do the collection | Yes | number |
colltndt | Collection date (default=earliest batch) **[1]* | No | string |
prenotify | Optional parameter, it's existence will trigger the prenotification towards the debtor (true/false) | No | boolean |
until | Only include transactions in the batch created until a specific date - epoch in milliseconds | No | number |
**[*1]**: When you set the collection date to a date in the future, all the batches from the current day until the colltndt are directly sent to the bank in one batch.
Sample response:
{
"frstMsgId": null,
"rcurMsgId": "BE81ZZZ08502098492203301148072234"
}
Response Parameters
A batch was created and sent when at least the rcurMsgId or frstMsgId returns a value. When both values are null nothing was sent.
Name | Description |
---|---|
frstMsgId | First-type message id (deprecated) |
rcurMsgId | Recurring-type message id |
HTTP Response
Code | Description |
---|---|
200 | Request succeeded. The response returns the id referencing both first and recurring sdd send to bank |
400 | User error if parameter is given but not valid (available in apierror header and response) |
Error codes
Code | Description |
---|---|
err_no_such_ct | No template specified |
err_invalid_ct | Invalid template specified |
err_provide_account | No recurring gateway configured on the profile (ct) |
err_no_access_subscription | No connection to the bank in your subscription |
err_invalid_date | Invalid date |
err_invalid_params | Invalid parameters passed in the request |
Status Collection
This endpoint allows to retrieve an SDD batch. 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.
The final flag is important as it indicates whether or not there are still automatic actions going on. 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 where no more automatic actions can be performed. Here an action will be required on your part. False means that we still have actions pending to debit the debtor's account. Note that paid can be reverted by the bank.
HTTP Request
GET /creditor/collect
curl https://api.twikey.com/creditor/collect \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/collect");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
curl_setOpt($ch, CUSTOMREQUEST, 'GET');
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/collect',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorization = null; //collected through logIn
public void statusCollection(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/collect")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void statusCollection(){
RestClient client = new RestClient(host + "/creditor/collect");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Sdds":[
{
"id":1437939405111,
"pmtinfid": "mypmtid",
"tx":4, // Number of transactions
"amount":120.0, // Total amount of the transactions
"Entries": [
{
"e2eid": "mye2e",
"amount": 30.0,
"contractId": 7488, // internal reference to the contract
"mandateRef": "TWIKEYCORE22", // mandate reference
"msg": "testing",
"final": false, // Does Twikey have any outstanding actions ?
"state": "open" // No payment information received as of yet
},
{
"e2eid": "mye2e",
"amount": 30.0,
"contractId": 7488,
"mandateRef": "TWIKEYCORE22",
"msg": "testing",
"final": true,
"state": "paid", // Transaction was paid
"bkamount": 30.0, // Booked amount as stated in account information
"bkdate": "2015-07-27", // Booking date as stated in account information
"bkerror": null // No errors
},
{
"e2eid": "mye2e",
"amount": 30.0,
"contractId": 6358,
"mandateRef": "TWIKEYCORE21",
"msg": "test",
"final": false, // This is FYI, since we haven't exhausted yet all possibilities (will continue dunning)
"state": "error", // Transaction was paid but customer requested a refund
"bkamount": 30.0,
"bkdate": "2015-07-27",
"bkerror": "MD06" // Actual error code (refund request)
},
{
"e2eid": "mye2e",
"amount": 30.0,
"contractId": 6358,
"mandateRef": "TWIKEYCORE21",
"msg": "test",
"final": true, // Could not collect, please contact your customer
"state": "error",
"bkamount": 30.0,
"bkdate": "2015-07-27",
"bkerror": "AG01"
}
]
}
]
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
id | Specific SDD reference | No | number |
pmtinfid | Specific Payment identifier | No | string |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
429 | Too many requests |
Error Codes
Error | Description |
---|---|
err_call_in_progress | Request already in progress by another client |
Import Collection
Import a direct debit batch for collection. When the batch is imported and valid, it is directly sent to the bank for processing. Accepted format: Pain 008.001.02 (ISO 20022)
A profile id needs to be passed using the ct
query parameter.
HTTP Request
POST /creditor/collect/import?ct=1234
The Direct Debit message can be imported as file or as XML in the body of the request.
curl --location --request POST 'https://api.twikey.com/creditor/collect/import?ct=1234' \
-h 'authorization: **authorization**' \
-h 'Content-Type: application/xml' \
-d '@path/to/batch.xml'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/collect/import?ct=1234',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => "<file contents here>",
CURLOPT_HTTPHEADER => array(
'Authorization: $authorization',
'Content-Type: application/xml'
),
));
$response = curl_exec($curl);
$result = json_decode($response);
curl_close($curl);
var data = "<file contents here>",
authorization = null; //collected through login
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://api.twikey.com/creditor/collect/import?ct=1234");
xhr.setRequestHeader("Authorization", authorization);
xhr.setRequestHeader("Content-Type", "application/xml");
xhr.send(data);
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorization = null; //collected through logIn
public void importCollection(){
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/xml");
RequestBody body = RequestBody.create(mediaType, "<file contents here>");
Request request = new Request.Builder()
.url(host + "/creditor/collect/import?ct=1234")
.method("POST", body)
.addHeader("Authorization", authorization)
.addHeader("Content-Type", "application/xml")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization = null; // collected through logIn
public void importCollection(){
var client = new RestClient(host + "/creditor/collect/import?ct=1234");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", authorization);
request.AddHeader("Content-Type", "application/xml");
request.AddParameter("application/xml", "<file contents here>", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
}
Query parameters
Parameter | Description | Required | Type |
---|---|---|---|
ct | The profile ID to import the collection on | Yes | Number |
HTTP Response
Code | Description |
---|---|
204 | The request has succeeded |
400 | Bad request |
Error codes
Code | Description |
---|---|
invalid_file | Invalid format or file already uploaded* |
*Collection with the same value of 'PmtInfId' and 'EndToEndId' was already uploaded.
Query collections
Create a search query to return all collections with a specific status, requested collection date, of a specific profile or a combination of different query parameters.
The response is limited to a set of 50 collections per page. The response returns the collections found based on your search query.
curl https://api.twikey.com/creditor/collect/query?ct=1234&reqcolldt=2024-05-06 \
-H 'authorization: **authorization**' \
$host = "https://api.twikey.com";
$ct = **ct_id**;$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/collect/query?ct=1234&reqcolldt=2024-05-06");
curl_setOpt($ch, CURLOPT_HTTPHEADER, "authorization: $authorization");
$server_output = curl_exec ($ch);
$result = json_decode($server_output);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/collect/query?ct=1234&reqcolldt=2024-05-06',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log(res);
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void updateFeed(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/collect/query?ct=1234&reqcolldt=2024-05-06")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorisation = null; // collected through logIn
public void updateFeed(){
RestClient client = new RestClient(host + "/creditor/collect/query?ct=1234&reqcolldt=2024-05-06");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"_links": {
"self": "/creditor/collect/query?ct=2223&reqcolldt=2024-05-06&page=0"
},
"collections": [
{
"id": 47737,
"pmtinfid": "BE81ZZZ0850209849-240503033438-2223",
"ct": 1234,
"tx": 25,
"amount": 578.55,
"status": "Sent",
"reqcolldt": "2024-05-06",
"generated": "2024-05-03",
"progress": "download"
},
{
"id": 47695,
"pmtinfid": "BE81ZZZ0850209849-240502080510-2223",
"ct": 1234,
"tx": 66,
"amount": 596.99,
"status": "Sent",
"reqcolldt": "2024-05-06",
"generated": "2024-05-02",
"progress": "sent"
}
]
}
HTTP Request
GET https://api.twikey.com/creditor/collect/query?parameter=value
Headers
By default a json response is returned, but you can opt to return the body in XML.
Header | Value | Description |
---|---|---|
Accept | application/xml | return the response in XML format instead of json |
Query Parameters
All parameters are optional. A combination of these parameters is possible.
Name | Description | Required | Type |
---|---|---|---|
state | sent, archived or cancelled | No | string |
generated | Creation date of the collection batch (YYYY-MM-DD) | No | date |
reqcolldt | Requested collection date of the batch (YYYY-MM-DD) | No | date |
ct | Profile ID | No | number |
page | Pagination is returned at the beginning of the response, include 'page' with a page number to go to the next or previous set of results | No | number |
from | Collections created from (including) a precise date local date (YYYY-MM-DD) | No | date |
until | Collections created until (excluding) a precise date local date (YYYY-MM-DD) - default is today | No | date |
until and from
Example: until=2024-05-30
and from=2024-05-31
to include everything from 2024-05-30 00:00:00 to 2024-05-30 23:59:59.
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) |
429 | Too many requests |
Error codes
Code | Description |
---|---|
err_invalid_state | Invalid state passed |
err_missing_params | Missing parameters or incorrect parameter value passed. |
Invoices
Create invoice
Create a new invoice for a customer. The invoice can be sent to your customer automatically or manually. Depending on the selected template, different payment options are provided to the customer.
It is possible to create invoices with a zero amount. Those will directly be set on 'paid' after import.
HTTP Request
POST /creditor/invoice
curl -X POST https://api.twikey.com/creditor/invoice \
-H 'authorization: **authorization**' \
-H "Content-Type: application/json" \
-d '{
"number": "Inv20200001",
"title": "Invoice July",
"remittance": "596843697521",
"ct": 1988,
"amount": 100,
"date": "2020-01-31",
"duedate": "2020-02-28",
"locale": "nl",
"customer": {
"customerNumber": "customer123",
"email": "no-reply@twikey.com",
"firstname": "Twikey",
"lastname": "Support",
"address": "Stationstraat 43",
"city": "Gent",
"zip": "9000",
"country": "BE",
"l": "fr",
"mobile": "32498665995"
},
"pdf": "JVBERi0xLj....RU9GCg=="
}'
**Or by referencing an existing document or customerNumber**
curl -X POST https://api.twikey.com/creditor/invoice \
-H 'authorization: **authorization**' \
-H "Content-Type: application/json" \
-d '{
"number": "Inv20200001",
"title": "Invoice July",
"remittance": "596843697521",
"ct": 1988,
"amount": 100,
"date": "2020-01-31",
"duedate": "2020-02-28",
"customerByRef": "customer123" // or "customerByDocument": "MandateReference123"
"pdf": "JVBERi0xLj....RU9GCg=="
}'
Sample response:
{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}
Sample response when the invoice already exist:
{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}
$client = new http\Client;
$request = new http\Client\Request;
$request->setRequestUrl('http://api.twikey/creditor/invoice');
$request->setRequestMethod('POST');
$body = new http\Message\Body;
$body->append('{
"number": "Invss123",
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"title": "Invoice April",
"remittance": "123456789123",
"ct": 60,
"amount": 100,
"date": "2020-03-20",
"duedate": "2020-04-28",
"locale": "nl",
"customer": {
"customerNumber": "customer123",
"email": "no-reply@twikey.com",
"firstname": "Twikey",
"lastname": "Support",
"address": "Stationstraat 43",
"city": "Gent",
"zip": "9000",
"country": "BE",
"lang": "fr",
"mobile": "32498665995"
}
}');
$request->setBody($body);
$request->setOptions(array());
$request->setHeaders(array(
'Authorization' => '...',
'Content-Type' => 'application/json'
));
$client->enqueue($request)->send();
$response = $client->getResponse();
echo $response->getBody();
Response:
'{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}'
var request = require('request');
var options = {
'method': 'POST',
'url': 'http://api.twikey/creditor/invoice',
'headers': {
'Authorization': '.....',
'Content-Type': ['application/json']
},
body: {
"number": "Invss123",
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"title": "Invoice April",
"remittance": "123456789123",
"ct": 60,
"amount": 100,
"date": "2020-03-20",
"duedate": "2020-04-28",
"locale": "nl",
"customer": {
"customerNumber": "customer123",
"email": "no-reply@twikey.com",
"firstname": "Twikey",
"lastname": "Support",
"address": "Stationstraat 43",
"city": "Gent",
"zip": "9000",
"country": "BE",
"lang": "fr",
"mobile": "32498665995"
}
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
Response:
{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n" +
" \"number\": \"Invss123\",\n" +
" \"id\" : \"fec44175-b4fe-414c-92aa-9d0a7dd0dbf2\",\n" +
" \"title\": \"Invoice April\",\n" +
" \"remittance\": \"123456789123\",\n" +
" \"ct\": 60,\n" +
" \"amount\": 100,\n" +
" \"date\": \"2020-03-20\",\n" +
" \"duedate\": \"2020-04-28\",\n" +
" \"customer\": {\n" +
" \"customerNumber\": \"customer123\",\n" +
" \"email\": \"no-reply@twikey.com\",\n" +
" \"firstname\": \"Twikey\",\n" +
" \"lastname\": \"Support\",\n" +
" \"address\": \"Stationstraat 43\",\n" +
" \"city\": \"Gent\",\n" +
" \"zip\": \"9000\",\n" +
" \"country\": \"BE\",\n" +
" \"lang\": \"nl\",\n" +
" \"mobile\": \"32498665995\"\n" +
" }\n" +
"}");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/invoice")
.method("POST", body)
.addHeader("Authorization", "701fbbfd-2681-47c3-8077-59345df335f2")
.addHeader("Content-Type", "application/json")
.build();
Response response = client.newCall(request).execute();
// Response:
'{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}'
var client = new RestClient("http://api.twikey/creditor/invoice");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "701fbbfd-2681-47c3-8077-59345df335f2");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n" +
" \"number\": \"Invss123\",\n" +
" \"id\" : \"fec44175-b4fe-414c-92aa-9d0a7dd0dbf2\",\n" +
" \"title\": \"Invoice April\",\n" +
" \"remittance\": \"123456789123\",\n" +
" \"ct\": 60,\n" +
" \"amount\": 100,\n" +
" \"date\": \"2020-03-20\",\n" +
" \"duedate\": \"2020-04-28\",\n" +
" \"locale\": \"nl\",\n" +
" \"customer\": {\n" +
" \"customerNumber\": \"customer123\",\n" +
" \"email\": \"no-reply@twikey.com\",\n" +
" \"firstname\": \"Twikey\",\n" +
" \"lastname\": \"Support\",\n" +
" \"address\": \"Stationstraat 43\",\n" +
" \"city\": \"Gent\",\n" +
" \"zip\": \"9000\",\n" +
" \"country\": \"BE\",\n" +
" \"lang\": \"fr\",\n" +
" \"mobile\": \"32498665995\"\n" +
" }\n" +
"}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
// Response
Response:
'{
"id": "fec44175-b4fe-414c-92aa-9d0a7dd0dbf2",
"number": "Inv20200001",
"title": "Invoice July",
"ct": 1988,
"amount": 100.00,
"date": "2020-01-31",
"duedate": "2020-02-28",
"status": "BOOKED",
"url": "https://app.twikey.com/<company_id>/fec44175-b4fe-414c-92aa-9d0a7dd0dbf2"
}'
Headers:
- X-Purpose: used to alter the url for specific use cases
- X-Partner: Allows you to add custom registration, for example: "Invoice was registered via webshop" (only displayed in the interface)
- X-Force-Transaction: boolean
- When true a transaction is created for the invoice regardless if the mandate is suspended or cancelled.
- Requires a reference to the document in the request (
"customerByDocument": "MNDT123"
) - The transaction will be under the To be sent transactions -> Uncollectable transactions when the mandate is not collectable
- When passing the parameter "manual" as true, this overrules the header.
Header | Value | Description |
---|---|---|
X-PURPOSE | qr (by default) | Returns a url that can be used in different bank apps. |
X-PURPOSE | redirect | Returns a branded-url for generic use cases (faster) |
X-PURPOSE | merchant | Returns a url that can be used over the counter. The payment page includes a large QR code on screen |
X-PARTNER | string | Alter 'Api' in 'Invoice was registered via Api' (only visible in the interface) |
X-FORCE-TRANSACTION | boolean | Create a transaction for the invoice when the mandate is suspended or cancelled (requires a reference to a mandate in the body of the request |
Request Parameters
Invoice Object
language:
The language in the invoice object can be passed using the parameter 'l' or 'locale'. When language is not passed:
- Existing customer: The language of the customer is used
- New customer: the default language of the invoice is as defined in your Company information configuration.
Specific locales:
- nl: Dutch (Belgium)
- fr: French (Belgium)
- nl_nl: Dutch (Netherlands)
- fr_fr: French (France)
- de: German
- it: Italian
- pt: Portuguese
- es: Spanish
The language passed in the customer object has no impact on the language of the invoice.
Name | Description | Required | Type | Max. |
---|---|---|---|---|
id | UUID of the invoice (optional) | No | string | 36 |
number | Invoice number [1] | Yes | string | 50 |
title | Message to the debtor [2], if empty one is generated using the invoice number.[2a] | No | string | 140 |
remittance | Payment message, if empty then title will be used [3] | No | string | 140 |
ref | Your internal reference | No | string | |
ct | Template to be used [4] | No | string | 4 |
amount | Amount to be billed | Yes | number | |
date | Invoice date | Yes | date | |
duedate | Due date | Yes | date | |
locale | Language of the invoice (en, nl, nl_nl, fr, fr_fr, de ,it ,pt, es) | No | string | 2 - 5 |
customer | Customer | Yes | object | |
customerByDocument | Customer by document reference | No | string | |
manual | true: Do not auto collect the invoice via a recurring mechanism on creation. The invoice can still be linked to a mandate afterwards. | No | boolean | |
Base64 encoded document | No | string | ||
redirectUrl | Optional redirect after pay url. A specific app protocol is required (eg. http(s)://). Verify that your PSP supports the specific protocol. | No | url | |
Use a different invoicing email address for this invoice [5]. | No | string | 70 | |
relatedInvoiceNumber | Link a credit note to an existing invoice by number [6]. | No | string |
Customer Object
Name | Description | Required | Type | Max. |
---|---|---|---|---|
customerNumber | The customer number to link this invoice too (strongly advised) | Yes* | string/number | 50 |
Debtor's email | Yes* | string | 70 | |
firstname | Debtor's first name - if not known we will put unknown | No | string | 50 |
lastname | Debtor's last name - if not known we will put unknown | No | string | 50 |
companyName | Name of the company | No | string | 140 |
coc | Enterprise number of the company | No | string | 50 |
lang / l | Debtor's language | No | string | 2 |
address | Debtor's address (street + number) | No | string | 70 |
city | Debtor's city | No | string | 50 |
zip | Debtor's zipcode | No | string | 50 |
country | Debtor's country | No | string | 2 |
mobile | Debtor's mobile number place | No | string | 50 |
- [1]: If an invoice with this number already exist, it will return the invoice details instead.
- [2]: This is the message that your customer will see on their bank statement.
- [2a]: The generated title contains the word 'invoice' (in the language of the invoice) followed by a space and the invoice number.
- [3]: This will be the code used in the bank statements Twikey provides that you'll be able to import in your accounting package. Belgium: When 12 characters long and the checksum is correct, this is treated as a structured message (OGM)
- [4]: Contract Template: When passed: we use that template When not passed: we use the default template of either the customer if they already exist or the default template from your merchant environment in case of a new customer.
- [5]: Use a specific email address for this invoice. The email address of the customer is not impacted.
- [6]: Link the credit note to an existing invoice. You can do a refund of a credit note and link multiple credit notes to one invoice. More information can be found under Credit Notes
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) |
Possible states
Possible states | Description |
---|---|
BOOKED | The invoice created but not processed by the bank |
PENDING | The payment is in progress |
EXPIRED | The invoice has expired |
Error codes
Code | Description |
---|---|
err_no_contract | No mandate found |
err_debtor_not_found | Debtor was not found |
err_invalid_state | The mandate is not active |
err_invalid_state (credit notes) | The related invoice is not paid or no beneficiary account |
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 |
err_debtor_id_missing | A customer number or email address is missing in the customer object |
err_invalid_email | Email invalid |
err_invalid_name_length | first and/or last name is too long |
register_lastname_missing | 'lastname' is empty |
register_firstname_missing | 'firstname' is empty |
Credit notes
curl -X POST https://api.twikey.com/creditor/invoice \
-H 'authorization: **authorization**' \
-H "Content-Type: application/json" \
-d '{
"number": "CN20200001",
"title": "Credit note July",
"remittance": "596843697521",
"relatedInvoiceNumber": "IV20200001",
"ct": 1988,
"amount": -100,
"date": "2024-07-31",
"duedate": "2024-08-28",
"locale": "nl",
"customerByRef": "123456"
}'
The request to create a credit note is the same as for invoices.
You can use all the parameters available and use a negative amount instead to create a credit note.
Related invoice
In the body of the request you can add the parameter relatedInvoiceNumber
to link an existing invoice to that credit note. Multiple credit notes can be linked to the same invoice.
Booked invoices
When the related invoice is in a booked (or expired) state, the amount of the credit note is deducted from the invoice. The remaining amount of the invoice can still be paid by the debtor. The credit note is directly marked as paid.
Paid invoices
When the related invoice is paid, the amount of the credit note is not deducted from the invoice. The credit note remains in a booked state unless, a refund is done using this credit note.
Refund a credit note
A refund can be done using the action on invoice endpoint /creditor/invoice/<invoiceID>/action?type=refund
where the id is that of the credit note.
If the credit note is related to an invoice, the refund is done using the payment method used to pay that invoice.
the refund is processed differently depending on:
- the credit note is not linked to an invoice: Requires a beneficiary account on the customer
- the credit note is linked to an invoice:
- The invoice is paid via SDD: The iban of the mandate is used as beneficiary account
- The invoice is paid via a payment link: the refund is done via the PSP
- The invoice is paid in another way (mark as paid, wire transfer,..): a beneficiary account is required on the customer owning the credit note
Refunds created using the account of the mandate or beneficiary needs to be sent to the bank. Refunds via the PSP are done directly.
Update invoice
Update invoice details. Use the unique invoice id in the request url.
HTTP Request
PUT /creditor/invoice/{{invoice_uid}}
curl -X PUT https://api.twikey.com/creditor/invoice/{{id}}
-H 'authorization: **authorization**' \
-H "Content-Type: application/json" \
-d '{
"title": "Invoice Updated",
"date": "{{invoiceDate}}",
"duedate": "{{dueDate}}",
"ref": "reference",
"pdf": "JVBERi...RU9GCg==",
"status": "paid",
"extra": {
"myAttribute": "new value"
}
}'
Sample response:
{
"id": "eff74b4e-5f9b-43de-ac1c-77d52736c66f",
"number": "49954184",
"title": "Invoice Updated",
"remittance": "+++211/6853/5187+++",
"ref": "reference",
"state": "PAID",
"amount": 6.0,
"date": "2022-03-30",
"duedate": "2022-04-13",
"ct": 2223,
"url": "https://app.twikey.com/11475/eff74b4e-5f9b-43de-ac1c-77d52736c66f"
}
Request parameters
Name | Description | Required | Type |
---|---|---|---|
title | Title of the invoice | No | String |
date | Invoice date | Yes | String |
duedate | Invoice due date | Yes | String |
ref | Invoice reference | No | String |
Base64 encoded document | No | String | |
status | Mark invoice as "booked", "archived", "paid" | No | String |
extra | Include custom attribute(s) in the 'extra' object to add or update the value of your custom attribute | No | Object/String |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error codes
Code | Description |
---|---|
err_not_found | Invoice id not found |
err_invalid_date | date or due date is invalid |
err_invalid_state | Invoice state does not allow updates |
err_invalid_params | Invalid request |
Delete invoice
This request allows you to delete invoices using the uuid when the state is not paid or in progress.
If the invoice has a PDF attached, it is also removed.
HTTP Request
DEL /creditor/invoice/{{invoice_uuid}}
curl -X DEL https://api.twikey.com/creditor/invoice/{{uuid}}
-H 'authorization: **authorization**' \
Request parameters
Only the invoice unique identifier is required in the request url.
HTTP Response
Code | Description |
---|---|
204 | No content - invoice is deleted |
400 | Bad request |
Error codes
Code | Description |
---|---|
err_not_found | Invoice not found |
err_not_authorized | The state of the invoice does not allow removal (PAID or in PROGRESS) |
Invoice feed
Retrieve the list of updates on invoices that had changes since the last call.
curl https://api.twikey.com/creditor/invoice \
-H 'authorization: **authorization**'
{
"Invoices": [
{
"id": "b3340469-2bc6-4d20-b717-5d3015979038",
"number": "INVOICE_8317363890",
"title": "B8 Sales, Inc.",
"remittance": "B7-64-3C-22-37-4C",
"ref": "my reference",
"state": "PAID",
"amount": 1.8,
"date": "2020-10-12",
"duedate": "2020-10-13",
"ct": 2223,
"url": "https://app.twikey.com/<company_id>/b3340469-2bc6-4d20-b717-5d3015979038"
},
{
"Example including lastpayment and meta data": "",
"invoice data": "...",
"lastpayment": [
{
"action": "payment_fail",
"date": "2022-02-23",
"e2e": "CF4013-20220223103125095074696-0",
"iban": "BE86050472131450",
"id": 309712,
"method": "sdd",
"pmtinf": "BE81ZZZ0850209849-220223103125-2223",
"rc": "MS03"
},
{
"action": "payment_fail",
"date": "2022-02-25",
"e2e": "CF4013-20220223103125095074696-0",
"iban": "BE86050472131450",
"id": 309712,
"method": "sdd",
"pmtinf": "BE81ZZZ0850209849-220223103125-2223",
"rc": "MS03"
},
{
"action": "payment",
"date": "2022-02-27T10:31:32Z",
"link": 429077,
"method": "paylink"
}
],
"meta": {
"lastError": "MS03"
}
}
]
}
$host = "https://api.twikey.com";
$authorization = null; //collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/invoice");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/invoice',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void getTransactions(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/invoice")
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void getTransactions(){
RestClient client = new RestClient(host + "/creditor/invoice");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
GET /creditor/invoice
Request Parameters
include Parameter
The include parameter (optional) can be used several times with a different value to include additional information about the invoices in the response.
Example:
GET /creditor/invoice?include=lastpayment&include=meta&include=customer
lastpayment
Returns a collection of information about the last payment(s) received for an invoice. The response object can have multiple actions, one for each payment (attempt) done for the invoice.
Value | Description | Required | Type |
---|---|---|---|
meta | Include meta data in the response (more information below) | No | string |
customer | Include customer details | No | string |
lastpayment | List details about the method and state of executed payments | No | string |
Parameter | Description |
---|---|
method | the method used for (attempted) payment: sdd (Direct Debit), paylink (payment link), transfer (wire transfer) or manual (marked as paid by a user) |
action | The action received, a payment or payment_fail |
id | Transaction identifier when method is sdd |
e2e | End to End identifier when method is sdd |
pmtinf | Payment information when method is sdd |
iban | The account number from which the amount was debited when method is sdd or transfer |
bic | Bic code of the account number when method is transfer |
rc | Return code received from the bank when method is sdd (PAID or Error code) |
link | Paymentlink identifier when method is paylink |
msg | The invoice title when method is 'transfer' or a user-message when method is 'manual' |
date | Date of the action yyyy-mm-dd (timestamp included when method is paylink or manual) |
double | when the invoice was already paid and we received a payment for the same invoice (boolean) |
meta
Returns meta-information about the invoice.
Parameter | Description |
---|---|
reminderLevel | the number of reminders sent out for the invoice |
partial | payment link id used to pay a partial amount of the invoice. This is only possible when a specific payment link was created to do so. |
lastError | last error received from the bank in case of an Direct Debit transaction |
name | Company name of the debtor |
paymentMethod | the method used to pay the invoice (e.g.: visa, mastercard, bancontact, direct_debit,..) |
customer
Returns the full customer-object in the response.
Possible states
Possible states | Description |
---|---|
BOOKED | The invoice created but not processed by the bank |
PENDING | The payment is in progress |
PAID | The invoice has been paid |
EXPIRED | The invoice has expired |
ARCHIVED | The invoice has been archived |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error Codes
Code | Description |
---|---|
err_call_in_progress | Request already in progress by another client |
Invoice details
Retrieve the details of an invoice. This can be done on either the invoice unique identifier or invoice number.
curl https://api.twikey.com/creditor/invoice/{{invoice}} \
-H 'authorization: **authorization**'
Sample response:
{
"id": "b3340469-2bc6-4d20-b717-5d3015979038",
"number": "INVOICE_8317363890",
"title": "B8 Sales, Inc.",
"remittance": "B7-64-3C-22-37-4C",
"ref": "my reference",
"state": "PAID",
"amount": 1.8,
"date": "2020-10-12",
"duedate": "2020-10-13",
"ct": 2223,
"url": "https://app.twikey.com/<company_id>/b3340469-2bc6-4d20-b717-5d3015979038"
}
$host = "https://api.twikey.com";
$authorization = null; //collected through logIn
$invoice = "invoice123";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/invoice/$invoice");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/invoice/invoice123',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null, //collected through logIn
invoice = "invoice123";
public void getTransactions(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/invoice/" + invoice)
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null, // gathered through logIn
invoice = "invoice123";
public void getTransactions(){
RestClient client = new RestClient(host + "/creditor/invoice/" + invoice);
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
GET /creditor/invoice/{{invoice}}
Request Parameters
include Parameter
The include parameter (optional) can be used several times with a different value to include additional information about the invoices in the response.
Example:
GET /creditor/invoice/{{invoiceID_or_Number}}?include=lastpayment&include=meta&include=customer
lastpayment
Returns a collection of information about the last payment(s) received for an invoice. The response object can have multiple actions, one for each payment (attempt) done for the invoice.
Parameter | Description |
---|---|
method | the method used for (attempted) payment: sdd (Direct Debit), paylink (payment link), transfer (wire transfer) or manual (marked as paid by a user) |
action | The action received, a payment or payment_fail |
id | Transaction identifier when method is sdd |
e2e | End to End identifier when method is sdd |
pmtinf | Payment information when method is sdd |
iban | The account number from which the amount was debited when method is sdd or transfer |
bic | Bic code of the account number when method is transfer |
rc | Return code received from the bank when method is sdd (PAID or Error code) |
link | Paymentlink identifier when method is paylink |
msg | The invoice title when method is 'transfer' or a user-message when method is 'manual' |
date | Date of the action yyyy-mm-dd (timestamp included when method is paylink or manual) |
double | when the invoice was already paid and we received a payment for the same invoice (boolean) |
meta
Returns meta-information about the invoice.
Parameter | Description |
---|---|
reminderLevel | the number of reminders sent out for the invoice |
partial | payment link id used to pay a partial amount of the invoice. This is only possible when a specific payment link was created to do so. |
lastError | last error received from the bank in case of an Direct Debit transaction |
name | Company name of the debtor |
customer
Returns the full customer-object in the response.
Possible states
Possible states | Description |
---|---|
BOOKED | The invoice created but not processed by the bank |
PENDING | The payment is in progress |
PAID | The invoice has been paid |
EXPIRED | The invoice has expired |
ARCHIVED | The invoice has been archived |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
429 | Too many requests |
Error Codes
Code | Description |
---|---|
err_call_in_progress | Request already in progress by another client |
Action on Invoice
Action request enables to send the invitation or reminder to a customer, reoffer invoices and do a refund for credit notes. Invitations and reminders can be done by sms or email. The reminder level (1 - 4) is based accordingly on prior reminders already send to the customer. Type smsreminder has only one reminder level.
HTTP Request
POST /creditor/invoice/{{id}}/action
curl -X POST https://api.twikey.com/creditor/invoice/${invoiceId}/action \
-H 'authorization: **authorization**' \
-d 'type=sms'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.twikey.com/creditor/invoice/$invoiceId/action",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "type=sms",
CURLOPT_HTTPHEADER => array(
"Authorization: $authorization",
"Content-Type: application/x-www-form-urlencoded"
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "type=sms");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/invoice/" + invoiceId)
.method("POST", body)
.addHeader("Authorization", authorization)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/invoice/{invoiceId}/action");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "{authorization}");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("type", "sms");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
type | The type of action | yes | string |
Type values
Name | Description |
---|---|
Send invitation by email | |
sms | Send invitation by sms |
reminder | Send a reminder by email |
smsreminder | Send a reminder by sms |
letter | Send only the invoice letter via postal service (letter) |
letterWithInvoice | Send both the letter and invoice via postal service (letter and invoice PDF) |
invoice | Send only the invoice via postal service (invoice PDF) |
reoffer | Offer the invoice to the bank for collection via SDD (transactions still needs to be sent to the bank) |
refund | Refund a credit note *1. |
peppol | Send the invoice over the Peppol network *2 |
- *1: Refund is processed differently depending on:
- the credit note is not linked to an invoice: Requires a beneficiary account on the customer
- the credit note is linked to an invoice:
- The invoice is paid via SDD: The iban of the mandate is used as beneficiary account
- The invoice is paid via a payment link: the refund is done via the PSP
- The invoice is paid in another way (mark as paid, wire transfer,..): a beneficiary account is required on the customer owning the credit note
Refunds created using the account of the mandate or beneficiary needs to be sent to the bank. Refunds via the PSP are done directly.
- *2: Sending via Peppol:
- Depending on your company configuration, the invoice is sent via email as backup when:
- The customer is not reachable via Peppol
- The customer is reachable but, the delivery to Peppol failed (invalid UBL for example)
- Depending on your company configuration, the invoice is sent via email as backup when:
HTTP Response
Code | Description |
---|---|
204 | The request has succeeded |
Error codes
Code | Description |
---|---|
err_not_found | Invoice not found |
err_invalid_email | Debtor has no email configured |
err_invalid_mobile | Debtor has no mobile number configured |
err_invalid_type | Type was not defined or invalid |
err_contact_support | Most commonly the requested type is not enabled |
err_no_credit_note | The invoice is not a credit note. only a credit note can be refunded. |
err_invalid_state | The state of the invoice or the state of the related invoice (to a credit note) does |
not allow the action. | |
no beneficiary account | A beneficiary account is required to refund the credit note |
Upload UBL
Invoices can be uploaded via UBL directly.
HTTP Request
POST /creditor/invoice/ubl
Content-Type: application/xml
curl -X POST https://api.twikey.com/creditor/invoice/ubl \
-H 'authorization: **authorization**' \
-H "Content-Type: application/xml"
Sample response:
{
"id": "30dc0701-0ee0-4f02-b268-1956e649f33d",
"number": "INVOICE_2103386169",
"title": "Facture 2103386169",
"remittance": null,
"ref": null,
"state": "BOOKED",
"amount": 35.8,
"date": "2022-03-29",
"duedate": "2022-04-11",
"ct": 2223,
"url": "https://app.twikey.com/11475/30dc0701-0ee0-4f02-b268-1956e649d99d"
}
Headers
Header | Description | Value |
---|---|---|
X-MANUAL | When set on 'true' the invoice is not auto collected via a recurring mechanism on import. | boolean |
X-INVOICE-ID | Use your own UUID for the invoice instead of a Twikey generated id | uuid |
Mapping
When uploading invoices using UBL files, we map the following fields to their corresponding UBL elements. A reference to the customer is required, without it the file can't be imported. Here you can find the mapping:
customerNumber
customerNumber In this particular order:
- cac:AccountingCustomerParty > cbc:SupplierAssignedAccountID
- cac:AccountingCustomerParty > cbc:AdditionalAccountID
- cac:AccountingCustomerParty > cac:Party > cac:PartyIdentification > cbc:ID
- cac:AccountingCustomerParty > cac:Party > cac:PartyIdentification > cbc:CompanyID
- cac:AccountingCustomerParty > cbc:CustomerAssignedAccountID
* The mapping can be overruled by one fixed field you want to use for the customer number. Contact our support to enable this.
Other fields
Description | Mapping |
---|---|
Remittance (v2.1*) | cac:PaymentMeans > cbc:InstructionID |
language | cac:AccountingCustomerParty > cac:Party > cbc:Language |
cac:AccountingCustomerParty > cac:Party > cac:Contact > cbc:Email | |
mobile | cac:AccountingCustomerParty > cac:Party > cac:Contact > cbc:Telephone |
Company Name | cac:AccountingCustomerParty > cac:Party > cac:PartyName > cbc:Name |
coc In this particular order: | |
cac:AccountingCustomerParty > cac:PartyIdentification > cbc:Id | |
cac:AccountingCustomerParty > cac:PartyIdentification > cbc:CompanyId | |
street | cac:AccountingCustomerParty > cac:Party > cac:PostalAddress > cbc:StreetName |
city | cac:AccountingCustomerParty > cac:Party > cac:PostalAddress > cbc:CityName |
zip | cac:AccountingCustomerParty > cac:Party > cac:PostalAddress > cbc:PostalZone |
country | cac:AccountingCustomerParty > cac:Party > cac:PostalAddress > cbc:Country |
v2.1*: Field only supported for UBL versions 2.1
Language: Emails regarding the particular invoice are sent in the language provided during import. When the language is not provided:
- Invoices for existing customers are sent in the customer set language
- Invoices for new customers are sent in the language defined in your Company information
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | Bad request (user-error) |
Error codes
Code | Description |
---|---|
invalid_file | File is invalid or no file was provided |
Reconciliation
Generate Files
Reconciliation files can be generated in CAMT053 or CODA. Direct debit files are available 3 days after sending the transactions to the bank. Payment link files are available every day for payments done the prior day(s).
When using Mollie for your settlements the frequency can vary. Please contact Mollie for more information about settlements.
HTTP Request
POST /creditor/files
curl POST 'https://api.twikey.com/creditor/files' \
--h 'Authorization: .....' \
--d 'sdd=false' \
--d 'format=coda' \
--d 'paylink=true'
$ch = curl_init();
$authorization = null; // collected through logIn
curl_setopt_array($ch, array(
CURLOPT_URL => "https://api.twikey.com/creditor/files",
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "sdd=true&paylink=true&format=coda",
CURLOPT_HTTPHEADER => array(
'Authorization: $authorization'
),
));
$server_output = curl_exec($ch);
curl_close($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/files',
method: 'POST',
headers: {
'Authorization': authorization
},
body :{
'sdd:' 'true',
'paylink:' 'true',
'format:' : 'coda'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "sdd=false&format=coda&paylink=true");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/files")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Authorization", "....")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/files");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "....");
request.AddParameter("sdd", "false");
request.AddParameter("format", "coda");
request.AddParameter("paylink", "true");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Query parameters
Name | Description | Required | Type |
---|---|---|---|
sdd | Set true to generate files for recurring transactions (direct debit and credit card) | Yes | boolean |
paylink | Set true to generate paylink files | Yes | boolean |
format | the format of the file (see Formats below). Default is camt | No | String |
Formats:
- coda: Default coda format
- coda6: Detailed coda version
- camt: CAMT053
- mt940: MT940 Exact Online compatible
- mt940f: MT940 TwinField compatible
- csv
HTTP Response
Code | Description |
---|---|
204 | The request has succeeded |
400 | User error if parameter is given but not valid (available in apierror header and response) |
X-FILES-CREATED | Only included in the header when files are generated, returns the number of files generated |
Error codes
Code | Description |
---|---|
err_invalid_params | Parameters are invalid |
List Files
Return a list of reconciliation files. The list returned include the files generated since this request was lasted performed. To retrieve files older then this you can add a X-RESET header to reset the feed to an earlier time. The value of the header is in UTC (standard ISO) format.
HTTP Request
GET /creditor/files
curl GET 'https://api.twikey.com/creditor/files' \
--h 'Authorization: ...'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/files',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => array(
'Authorization: ...'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
myHeaders.append("Authorization", "...");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/files", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/files")
.method("GET", null)
.addHeader("Authorization", "...")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/files");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "...");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Sample response:
{
"Files": [
{
"name": "sdd_twikey_BE81ZZZ0000000000--201222090211-2223.cod",
"created": "2021-03-16T17:28:38Z"
},
{
"name": "sdd_twikey_BE81ZZZ0000000000--201222090212-2223.cod",
"created": "2021-03-16T17:28:38Z"
}
]
}
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error codes
Code | Description |
---|---|
err_not_authorized | Action not allowed. Contact your customer success agent. |
Download File
Download reconciliation files.
HTTP Request
GET /creditor/files/filename
curl GET 'https://api.twikey.com/creditor/files/sdd_twikey_BE81ZZZ0000000000--201222090211-2223.cod' \
--h 'Authorization: .....'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/files/sdd_twikey_BE81ZZZ0000000000-201222090211-2223.cod',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => array(
'Authorization: ....'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
myHeaders.append("Authorization", "....");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/files/sdd_twikey_BE81ZZZ0000000000--201222090211-2223.cod", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/files/sdd_twikey_BE81ZZZ0000000000--201222090211-2223.cod")
.method("GET", null)
.addHeader("Authorization", "....")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/files/sdd_twikey_BE81ZZZ0000000000--201222090211-2223.cod");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "....");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Query parameters
Name | Description | Required | Type |
---|---|---|---|
filename | the filename | Yes | String |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
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. When specifying a redirectUrl, use http:// or https:// to refer to the website. If an email address is added, the debtor is also added as a customer in Twikey.
HTTP Request
POST /creditor/payment/link
curl -X POST https://api.twikey.com/creditor/payment/link \
-H 'authorization: **authorization**'\
-d 'email=no-reply@twikey.com'\
-d 'message=Faulty payments last month'\
-d 'amount=100'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/payment/link");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"email=no-reply@twikey.com"
."&message=Monthly payment"
."&amount=10");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/payment/link',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body :{
'email': 'no-reply@twikey.com',
'message': 'Monthly payment',
'amount': '10'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void addTransaction(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("email=no-reply@twikey.com")
.add("message", "Monthly payment")
.add("amount", "10")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/payment/link")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; //collected through logIn
public void addTransaction(){
RestClient client = new RestClient(host + "/creditor/payment/link");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"email=no-reply@twikey.com" +
"message=Monthly payment" +
'amount=10"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"id": 1,
"amount": 55.66,
"msg": "Test",
"url": "https://mycompany.twikey.com/payment/tr_l2iKz0LT8HvRrmf0"
}
Header X-Purpose:
Use X-Purpose to alter the url for specific use cases. When using the x-purpose qr the generated url is one that can be used to create a qr-code which can be scanned by most bank apps to access the payment page.
In case you want to make the url and QR-code yourself without extra calls, the url is constructed using 'app.twikey.com', your company id and the transaction id, example:https://app.twikey.com/1234/tr_l2iKz0LT8HvRrmf0
Value | Description |
---|---|
qr | Returns a url that can be scanned in different bank apps. |
redirect(default) | Returns a branded-url for generic use cases (faster) |
Request Parameters
At least a customer number, email or a name is required to be able to create a paymentlink.
Name | Description | Required | Type |
---|---|---|---|
customerNumber | The customer number (strongly advised) | No | string |
Email of the debtor | No (Required to send invite) | string | |
lastname | lastname of the customer | No | string |
firstname | firstname of the customer | No | string |
companyName | The company name (if debtor is company) | No | string |
coc | The enterprise number (if debtor is company) | No | string |
l | Language (en, nl, nl_nl, fr, fr_fr, de ,it ,pt, es) | No | string |
mobile | mobile number | No | String |
ct | contract template | No | String |
title | Message to the debtor [*1] | Yes | string (200) |
remittance | Payment message, if empty then title will be used [*2] | No | string |
amount | Amount to be billed | Yes | string |
redirectUrl | Optional redirect after pay url. A specific app protocol is required (eg. http(s)://). Verify that your PSP supports the specific protocol. | No | url |
place | Optional place | No | string |
expiry | Optional expiration date | No | date |
sendInvite | Send out invite email or sms directly (email, sms) [*3] | No | string |
address | Address (street + number) | No | string |
city | City of debtor | No | string |
zip | Zipcode of debtor | No | string |
country | ISO format (2 letters) | No | string |
txref | Reference (ref) from existing unpaid transactions | No | string |
method | Circumvents the payment selection with PSP (see supported methods) | No | string |
invoice | Create payment link for specific invoice number | No | string |
isTemplate | Use a (custom) payment form [*4]. | No | boolean |
Own attribute | Custom attributes defined on your template can also be used. | No | As defined on your template |
[1]: This is the message that your customer will see on their bank statement. (More info..)
[2]: This will be the code used in the bank statements Twikey provides that you'll be able to import in your accounting package.
[3]: A registered email can be sent using the value 'registeredEmail' (requires Registered-email integration).
[4]: When set on 'true' the customer is redirected to a (custom) payment form. For more information about template links, please contact support.
supported methods:
- bancontact, ideal, maestro, mastercard, visa, inghomepay, kbc, belfius, paypal, googlepay, applepay, banktrans
language invitations:
The language of the invitation is defined on the language in combination of the country code parameter. It can also be passed explicitly using only the language parameter:
- nl: Dutch (Belgium)
- fr: French (Belgium)
- nl_nl: Dutch (Netherlands)
- fr_fr: French (France)
- de: German
- it: Italian
- pt: Portuguese
- es: Spanish
Expiry date:
A timestamp (UTC) can also be included in the expiry date, but this is only supported by certain PSP's. For more information contact our support.
Custom attributes:
Custom attributes defined on your template can also be used as parameters in the request. This is then also returned in the paymentlink feed/detail request.
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_invalid_amount | Invalid Amount given |
err_invalid_date | Invalid Expiry Date |
err_duplicate_tx | Duplicate transaction |
err_not_found | Invoice not found or invoice amount is exceeded |
err_invalid_state | Invoice state is not booked or expired |
err_invalid_email | Email is invalid |
err_invalid_name | Name is invalid |
err_msg_missing | Title is missing |
err_no_integration | One-off payment gateway is not configured on the profile (ct) |
Special cases:
Send payment links using sms.
For this you have to include a mobile number and email address in the request. The parameter 'sendInvite' has to be set on 'sms'.
Create payment link for invoices
It is possible to create payment links for booked or expired invoices. This can be done for the total amount of the invoice, or a partial amount.
In case of a partial payment, the payment link id is displayd in the invoice feed meta data ('partial': 'link-id'). Add the parameter 'invoice' and use the invoice number to create a payment link for invoices.
Important: When the customer use that link and pays, the invoice is marked as paid even in case of a partial amount.
Payment link for a SDD transaction
In order to create a payment link for SDD transactions use either 'tx' (for the id of the transaction) or 'txref' for the reference. Note however that the transaction can only be linked when it is in 'failed' state.
Paymentlink Feed
Get payment link feed since the last retrieval
HTTP Request
GET /creditor/payment/link/feed
curl -X GET https://api.twikey.com/creditor/payment/link/feed \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/payment/link/feed?all="");
curl_setopt($ch, CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/payment/link/feed',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void getTransactions(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/payment/link/feed")
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void getTransactions(){
RestClient client = new RestClient(host + "creditor/payment/link/feed");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Sample response:
[
{
"amount": 17.95,
"id": 3354,
"msg": "Betaling Factuur 123",
"ref": "J7F079OG00000",
"state": "paid"
},
{
"amount": 19.95,
"id": 3821,
"msg": "Rappel Factuur 101",
"ref": "KJEVMD5",
"state": "paid"
}
]
Sideloading customer, meta and time:
{
"links": [
{
"id": 228192,
"ct": 2223,
"amount": 12.19,
"msg": "Order-id 10058",
"ref": "R10058",
"state": "paid",
"customer": {
"id": 1010238,
"email": "email@example.com",
"firstname": "Smith,",
"lastname": " Douglas",
"address": "Stationstraat 43",
"city": "Gent",
"zip": "9000",
"country": "BE",
"customerNumber": "de8f0be99b1b2521c7a74a5067b5d85f",
"l": "nl",
"mobile": null
},
"meta": {
"active": false,
"sdd": "130542",
"tx": "255733"
},
"time": {
"creation": "2021-05-03T07:42:40Z",
"expiration": "2021-05-17T07:42:40Z",
"lastupdate": "2021-05-03T07:44:00Z"
}
},
{
"id": 244041,
"ct": 2223,
"amount": 449.87,
"msg": "Invoice February",
"ref": "invoice-2021-02",
"state": "paid",
"customer": {
"id": 1200553,
"email": "email@example.com",
"firstname": "John",
"lastname": "Smith",
"address": "Stationstraat 43",
"city": "Gent",
"zip": "9000",
"country": "BE",
"customerNumber": "5921071",
"l": "nl",
"mobile": "+3283838383"
},
"meta": {
"active": false,
"method": "bancontact",
"invoice": "d133b08b-fe06-445b-81ff-6846a568d71f"
},
"time": {
"creation": "2021-05-06T15:16:52Z",
"lastupdate": "2021-05-06T15:16:55Z"
}
}
]
}
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
all | Include all non-paid updates too (by default only paid updates are returned) | No | boolean |
include parameter
The 'include' parameter can be added multiple times with different values
Value | Description | Required | Type |
---|---|---|---|
customer | customer detail | No | string |
meta | meta data | No | string |
time | time data | No | string |
refunds | return all the refunds done for a payment link |
Possible states
States | Description |
---|---|
Created | The payment link has been created but the customer did not do anything with it yet. |
Started | The customer has clicked on the payment link you transmitted him but did not complete the payment. In the Twikey dashboard the state will be shown as clicked. |
Pending | The customer started to pay, but did not complete the payment process. (only for iDeal, Paypal & Tikkie) |
Declined | The customer completed the payment process but the payment wasn't successful |
Paid | The customer paid the amount asked. |
Refunded | The payment link was refunded |
Expired | The customer did not complete the payment before the expiration date you defined when creating the payment link. |
Response Parameters
meta
Payment links used to pay invoices or direct debit transactions (in case of dunning for example) will include additional meta data. Dependant the case, different response parameters are returned.
Parameter | Description |
---|---|
active | the payment link url is still valid (true, false) |
sdd | internal sdd identifier (Twikey) |
tx | transaction id returned when linked to a transaction |
method | the method of payment. Only returned when linked to an invoice or if explicitly passed when creating a paymentlink. |
paymentMethod | returns the used payment method when we received it from the payment provider |
type | the payment method returned by the PSP. Only returned if we received it (PSP dependant). |
invoice | invoice unique identifier returned when linked to an invoice |
time
Parameter | Description |
---|---|
creation | creation of the payment link |
expiration | expiry of the payment link. Not returned when there is none |
lastupdate | last time the payment link was updated |
A payment link can have a state 'active': false when the expiration date is not set or not yet reached. The link is then already invalidated in a different way (paid, archived,..).
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error Codes
Code | Description |
---|---|
err_call_in_progress | Request already in progress by another client |
Status paymentlink
Get status of a payment link
HTTP Request
GET /creditor/payment/link
curl https://api.twikey.com/creditor/payment/link?id=123 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/payment/link?id=123");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/payment/link?id=123',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void addTransaction(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/payment/link/id=123")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; //collected through logIn
public void addTransaction(){
RestClient client = new RestClient(host + "/creditor/payment/link?id=123");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Sample response:
[{
"id": 1,
"amount": 55.66,
"msg": "Test", // title of the paymentlink
"ref": "My Ref", // remittance of the paymentlink
"state": "paid" // one of created, started, declined, paid, pending, expired
}]
Query Parameters
Either id or ref is mandatory.
Name | Description | Required | Type |
---|---|---|---|
id | Id of the link | No | string |
ref | Your reference | No | string |
Include meta
You can use the additional query parameter include=meta
to return additional information about the payment link.
Payment links used to pay invoices or direct debit transactions (in case of dunning for example) will include additional meta data. Dependant the case, different response parameters are returned:
Parameter | Description |
---|---|
active | the payment link url is still valid (true, false) |
sdd | internal sdd identifier (Twikey) |
tx | transaction id returned when linked to a transaction |
method | the method of payment. Only returned when linked to an invoice or if explicitly passed when creating a paymentlink. |
paymentMethod | returns the used payment method when we received it from the payment provider |
type | the payment method returned by the PSP. Only returned if we received it (PSP dependant). |
invoice | invoice unique identifier returned when linked to an invoice |
time | return date and time events (creation, last updated, expiry) |
Include refunds
You can use the additional query parameter include=refunds
to return all refunds done for a payment link.
It is possible to combine different includes at once, example: /creditor/payment/link?id=123&include=meta&include=refunds
Possible states
States | Description |
---|---|
created | The payment link has been created but the customer did not do anything with it yet. |
started | The customer has clicked on the payment link you transmitted him but did not complete the payment. In the Twikey dashboard the state will be shown as clicked. |
pending | The customer started to pay, but did not complete the payment process. (only for iDeal, Paypal & Tikkie) |
declined | The customer completed the payment process but the payment wasn't successful |
paid | The customer paid the amount asked. |
expired | The customer did not complete the payment before the expiration date you defined when creating the payment link. |
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) |
429 | Too many requests |
Error codes
Code | Description |
---|---|
err_missing_params | No parameter given |
err_no_transaction | No link found based on the transaction |
err_not_found | No link found |
Refund paymentlink
Refund the full or partial amount of a payment link.
curl -x POST 'https://api.twikey.com/creditor/payment/link/refund' \
-H 'authorization: **authorization**'\
-D 'id=123' \
-D 'message=refund' \
-D 'amount=25'
HTTP Request
POST /creditor/payment/link/refund
Supported PSP
Name | Supported |
---|---|
Mollie | Yes |
MultiSafePay | Yes |
Buckaroo | Yes |
CCV | Yes **[1]* |
- **[*1]**: Only when a specific
method
was passed when creating the payment link.
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
id | Paymentlink ID | Yes | string |
message | Refund message | Yes | String |
amount | Full or partial amount (if not passed, full amount is used) | No | number |
Sample response:
{
"id": 10.23,
"amount": 25.0,
"msg": "refund"
}
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error codes
Code | Description |
---|---|
err_contact_support | Error details in response "extra" |
err_fail_refund | Error details in response "extra" |
Remove paymentlink
Remove a payment link. Only paymentlinks with status 'created' can be removed, all other ones will be archived.
curl -X DELETE https://api.twikey.com/creditor/payment/link?id=123 \
-H 'authorization: **authorization**'
HTTP Request
DELETE /creditor/payment/link
Request Parameters
Name | Description | Required | Type |
---|---|---|---|
id | paymentlink ID | Yes) | string |
Meta data
Including meta data when you retrieve the status of one (or more) payment link(s) provides you with the following information:
- If the link was generated in a dunning step.
- If the link was generated in order to get an invoice paid
Sample:
{
"Links": [
{
"id": 77920,
"ct": 2223,
"amount": 6.0,
"msg": "123456789123",
"ref": "CF933-20200807073818839144-0",
"state": "created",
"meta": {
"sdd": "63884",
"tx": "1234567"
}
},
{
"id": 77955,
"ct": 2223,
"amount": 10.0,
"msg": "124512454",
"ref": "CF933-202008070738447439143-0",
"state": "created",
"meta": {
"tx": "1234567"
}
},
{
"id": 76667,
"ct": 2223,
"amount": 100.0,
"msg": "123456789123",
"ref": null,
"state": "paid",
"meta": {
"invoice": "a48c77a7-349e-424b-bdfc-baa748ee9e55"
}
}
]
}
Meta parameters
Name | Description |
---|---|
tx | The transaction for which this link was generated |
sdd | The sdd transaction (state = failed) send to the bank for wich this link was generated |
invoice | The invoice uuid linked to the payment link |
While the tx can be returned if the link was generated on a transaction, the sdd will only be returned in the context of dunning. To retrieve related transaction, use the returned 'tx' id in the transaction status request.
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/company.
If the customer was not known, you may need to create a beneficiary account prior to making this call. In order to avoid extra costs on bank side an additional call to create the batch may be necessary or can be done automatically upon request.
Create/add a new credit transfer
Create or add a new credit transfer. If the customer has a signed mandate or beneficiary account(s) you can create a refund directly. When this is not the case you will need to add a beneficiary account first.
The customer has a signed mandate
Then you can pass the parameter 'customerNumber' without IBAN in the request. We then add the account from the mandate as beneficiary account and create the refund.
The customer has a beneficiary account
In this case you only need to pass the 'iban' or 'customerNumber' parameter in the request. This will directly add the refund to that beneficiary account. We do advise to use the 'customerNumber' and 'iban' parameter together should you have multiple customers with the same iban account or a customer with multiple beneficiary accounts. Otherwise, we don't know for which customer or on which account the refund is and will add it on the first beneficiary account found for that iban or customer.
HTTP Request
POST /creditor/transfer
curl https://api.twikey.com/creditor/transfer \
-H 'authorization: authorization' \
-d 'iban=BE68068897250734' \
-d 'customerNumber=123' \
-d 'message=test%20credit%20transfer' \
-d 'ref'='123' \
-d 'amount='0.00'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"iban=BE68068897250734"
."&customerNumber=123"
."&message=test credit transfer"
."&amount="50.00"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfer',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"iban":"BE680688972.....",
"customerNumber":"123",
"message":"test credit transfer",
"amount":"50.00"
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("iban", "BE680688972.....")
.add("customerNumber","123")
.add("message","test credit transfer")
.add("amount", "50.00")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transfer")
.post(formBody)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfer");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"iban=BE68068897250734" +
"&customerNumber=123" +
"&message=test credit transfer" +
"&amount=50.00"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Entries": [
{
"id": "11DD32CA20180412220109485",
"iban": "BE68068097250734",
"bic": "JVBABE22",
"amount": 12,
"msg": "test",
"place": null,
"ref": "123",
"date": "2018-04-12"
}
]
}
Request Headers
Name | Description | type | max. length |
---|---|---|---|
Idempotency-Key | Unique key usable only once per request every 24hrs | string | 64 |
Request parameters
Name | Description | Required | Type |
---|---|---|---|
customerNumber | The customer number | No (if iban is passed) | string |
iban | Iban of the beneficiary | No (if customerNumber is passed) | string |
message | Message to the creditor | Yes | string (140) |
amount | Amount to be send | Yes | number |
ref | Reference of the transaction | No | String |
date | Required execution date of the transaction (ReqdExctnDt) | No | string |
place | Optional place | No | string |
*Note: Three combinations are possible with customerNumber and iban. Either one of the two parameters is required, but a customer needs at least one valid beneficiary account to do refunds.
- Both customerNumber and iban = Find a beneficiary account for the given customer with the given iban
- Only iban = Find the beneficiary account with the given iban
- Only customerNumber = Find the first beneficiary account of the given customer
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) |
409 | Conflict. Idempotency-key already used |
Error codes
Code | Description |
---|---|
err_invalid_iban | Invalid Iban |
err_invalid_state | Mandate existed, but was not active |
err_invalid_date | Invalid date |
err_invalid_sepachars | Invalid message |
err_invalid_amount | Invalid amount |
err_not_found | Customer not found |
err_invalid_params | Some parameter is invalid, see the extra in the response body for more information. |
err_duplicate_ref | Idempotency-key was already used in the last 24hrs. |
Get credit transfer feed
Retrieve list of credit transfers that have changes since the last call. Only when the credit transfers is in a PAID status it is returned in the feed.
curl https://api.twikey.com/creditor/transfer \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; // collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer");
curl_setopt($ch, CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, // collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfer',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void getTransactions(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transfer")
.get()
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // collected through login
public void getTransactions(){
RestClient client = new RestClient(host + "/creditor/transfer");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
HTTP Request
GET /creditor/transfer
Sample response:
{
"Entries": [
{
"id": "IBWICD6E44Y1N3YRMCVB2U7BA",
"iban": "BE70361272935897",
"bic": "GDOENFY",
"amount": 1000,
"msg": "credit transfer message",
"place": null,
"ref": "123",
"date": "2017-01-20",
"state": "PAID",
"bkdate": "2017-01-24"
},
{
"id": "X52D04L9DNNPWKCRGBV7YPV2R",
"iban": "BE93261223700539",
"bic": "BRODBZL",
"amount": 12345,
"msg": "credit transfer message",
"place": "Brugge",
"ref": "456",
"date": "2018-01-23",
"state": "PAID",
"bkdate": "2018-01-25"
},
{
"id": "ZE31QPMT6GRMHEY7IPOOD219R",
"iban": "BE4447226747140",
"bic": "ORNSRAM",
"amount": 250099,
"msg": "",
"place": "Ghent",
"ref": "789",
"date": "2017-03-23",
"state": "PAID",
"bkdate": "2017-03-24"
},
"..."
]
}
Query Parameters
Parameter | Description |
---|---|
include=seq | Return the sequence number in the feed for each entry. |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Details of a credit transfer
Retrieve the details of a credit transfer by id. The id is the e2e id of the credit transfer upon creating the credit transfer. You can find the E2E in the Twikey interface by going to the customer and opening the Refunds tab. The E2E is 'Your reference'.
Depending on the bank used for a refund batch, the response value of bkdate
can be a date or date with timestamp.
HTTP Request
GET /creditor/transfer/detail
curl https://api.twikey.com/creditor/transfer/detail?id=609C16C920180919083905923 \
-H 'authorization: authorization'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer/detail?id=609C16C920180919083905923");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfer/detail?id=609C16C920180919083905923',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
Request request = new Request.Builder()
.url(host + "/creditor/transfer/detail?id=609C16C920180919083905923")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfer/detail?id=609C16C920180919083905923");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Entries": [
{
"id": "609C16C920180919083905923",
"iban": "BE08001166979213",
"bic": "GEBABEBB",
"amount": 5,
"msg": "test",
"place": null,
"ref": "123",
"date": "2018-09-19",
"state": "PAID",
"bkdate": "2017-03-24"
}
]
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
id | id (e2e identifier) of the created refund | Yes | 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) |
429 | Too many requests |
Error codes
Code | Description |
---|---|
err_no_transaction | No such transaction |
Remove a credit transfer
Remove a single credit transfer.
HTTP Request
DELETE /creditor/transfer
curl -X DELETE https://api.twikey.com/creditor/transfer?id=123 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer?id=123");
curl_setopt($ch, CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transfer?id=123',
method: 'DELETE',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void removeTransaction(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transfer?id=123")
.delete(null)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void removeTransaction(){
RestClient client = new RestClient(host + "/creditor/transfer?id=123");
RestRequest request = new RestRequest(Method.DELETE);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
id | id of the created refund | Yes | 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_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 of the recurring gateway configured on an existing profile or, any other bank gateway account available in your Payment Hub (Refunds need to be enabled for that gateway).
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.
HTTP Request
POST /creditor/transfer/complete
curl https://api..twikey.com/creditor/transfer/complete \
-H 'authorization: authorization' \
-d 'ct=**ct_id**'
$host = "https://api.twikey.com";
$ct = "**ct_id**";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer"/complete);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "ct=$ct");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
ct= "**ct_id**",
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfer/complete',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"ct": ct
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String ct = "**ct_id**";
private String authorisation = null; //collected through logIn
public void completeCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("ct", ct)
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transfer/complete")
.post(formBody)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void completeCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfer/complete");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter(
"application/x-www-form-urlencoded",
"ct=" + ct,
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
ct | Profile containing the originating account (configured as recurring gateway) | Yes | string |
iban | Originating account, if different from ct account (any other bank account with refunds enabled from your Payment Hub) | No | string |
Sample response:
{
"CreditTransfers": [
{
"id": 2837,
"pmtinfid": "Twikey-20220330113125070605075",
"entries": 2
}
]
}
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | Invalid request |
Batch details
Get all details from a specific batch
HTTP Request
GET /creditor/transfer/complete
curl https://api..twikey.com/creditor/transfer/complete?id=2837&pmtinfid=Twikey-20220330113125070605075 \
-H 'authorization: authorization' \
$host = "https://api.twikey.com";
$id = "**id**";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfer/complete?id=123");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
ct= "**ct_id**",
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfer/complete?id=123',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void completeCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
Request request = new Request.Builder()
.url(host + "/creditor/transfer/complete?id=123")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void completeCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfer/complete?id=123");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
id | Id of the batch previously submitted | No | string |
pmtinfid | PmtInfId of the batch previously submitted | No | string |
Sample response:
{
"CreditTransfers": [
{
"id": 2837,
"pmtinfid": "Twikey-20220330113125070605075",
"progress": "download",
"entries": 2
}
]
}
Response Parameters
Name | Description |
---|---|
id | Batch identifier |
pmtinfid | Payment identifier |
progress | progress of the batch. received (by bank), download |
entries | credit transfers in this batch |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | Invalid request |
Error codes
Code | Description |
---|---|
err_invalid_params | No such batch available |
Get beneficiary accounts
Fetch a list of all customers' beneficiary accounts.
HTTP Request
GET /creditor/transfers/beneficiaries
curl https://api.twikey.com/creditor/transfers/beneficiaries?withAddress=true \
-H 'authorization: authorization'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfers/beneficiaries?withAddress=true");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfers/beneficiaries?withAddress=true',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
Request request = new Request.Builder()
.url(host + "/creditor/transfers/beneficiaries?withAddress=true")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfers/beneficiaries?withAddress=true");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"beneficiaries": [
{
"name": "sdfsf",
"iban": "BE92221216720939",
"bic": "GEBABEBB",
"available": true,
"address": null
},
{
"name": "beneficiary2",
"iban": "BE16645348971174",
"bic": "JVBABE22",
"available": true,
"address": {
"street": "Veldstraat 11",
"city": "Gent",
"zip": "9000",
"country": "BE"
}
}
]
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
withAddress | include addresses in reponse | Yes | boolean |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
500 | System error |
Add a beneficiary account
In order to be able to do a refund, one needs to have a beneficiary account. The account can be either created explicitly via this call or implicitly via a signed SDD mandate. In the latter case, the account from the mandate is taken so you can immediately call the refund without this call.
Creating a beneficiary account can be done for an existing customer or a new one. This is done based on the customerNumber or in its absence the email address. If a customer is found, the address will also be updated if address, zip, city and country are passed on in the call.
For new customers and therefor new accounts, it is strongly advised to add the customerNumber as it allows you to reference them later for updates or creation of other objects such as contracts/mandate and/or paymentlinks. An address and name are mandatory.
HTTP Request
POST /creditor/transfers/beneficiaries
curl -X POST https://api.twikey.com/creditor/transfers/beneficiaries \
-H 'authorization: authorization' \
-d 'customerNumber=123' \
-d 'email=support@twikey.com' \
-d 'name=Support Twikey' \
-d 'l=NL' \
-d 'mobile=32479123123' \
-d 'address=Stationstraat 43' \
-d 'zip="9051"' \
-d 'city=Sint Denijs Westrem' \
-d 'country=BE' \
-d 'iban=BE68068897250734' \
-d 'bic=JVBABE22'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfers/beneficiaries");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"&email=support%40twikey.com"
."&customerNumber=123"
."&name=Support Twikey"
."&l=NL"
."&address=Stationstraat%2043"
."&zip=9051"
."&city=Sint%20Denijs%20Westrem"
."&country=BE"
."iban=BE68068897250734"
."bic=JVBABE22"
);
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/transfers/beneficiaries',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"customerNumber": "134",
"email": "support@twikey.com",
"name": "Support Twikey",
"l": "NL",
"address": "Stationstraat 43",
"zip": "9051",
"city": "Sint Denijs Westrem",
"country": "BE",
"iban": "BE68068897250734",
"bic": "JVBABE22"
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("customerNumber", "134")
.add("email", "support@twikey.com")
.add("name", "Support Twikey")
.add("l", "NL")
.add("address", "Stationstraat 43")
.add("zip", "9051")
.add("city", "Sint Denijs Westrem")
.add("country", "BE")
.add("iban","BE68068897250734")
.add("bic", "JVBABE22")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/transfers/beneficiaries")
.post(formBody)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/transfers/beneficiaries");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded",
"&customerNumber=123" +
"&email=support%40twikey.com" +
"&name=Support Twikey" +
"&l=NL" +
"&address=Stationstraat%2043" +
"&zip=9051" +
"&city=Sint%20Denijs%20Westrem" +
"&country=BE" +
"iban=BE68068897250734" +
"bic=JVBABE22"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"name": "Beneficiary Name",
"iban": "BE68068897250734",
"bic": "JVBABE22",
"available": true,
"address": {
"street": "Veldstraat 11",
"city": "Gent",
"zip": "9000",
"country": "BE"
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
customerNumber | The customer number | No | string |
name | Firstname & lastname of the debtor | No | string |
Email of the debtor | No | string | |
l | language of the customer ISO format (2 letters) | No | string |
mobile | Mobile number required for sms (International format +32499123445) | No | string |
address | The street name and number/box | No (Yes for new customers) | string |
city | City of debtor | No (Yes for new customers) | string |
zip | Zipcode of debtor | No | string |
country | ISO format (2 letters) | No (Yes for new customers) | string |
companyName | The company name | No | string |
vatno | The enterprise number | No | string |
iban | IBAN of the beneficiary | Yes | string |
bic | BIC of the beneficiary | No | string |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | Validation error due to wrong user input |
Error codes
Code | Description |
---|---|
err_name_required | Invalid or missing name |
err_invalid_iban | Invalid IBAN number |
err_invalid_bic | Invalid BIC number |
register_address_missing | Address missing |
Disable a beneficiary account
Disable of a beneficiary account can be done via an IBAN number, but it's strongly advisable to add the customerNumber in the call. This is optional for backwards compatible reasons, but strongly recommended as an account may be used on multiple customers. Note that the the beneficiary will be disabled and not deleted. Reactivating a beneficiary account can only be done using the interface.
HTTP Request
DELETE /creditor/transfers/beneficiaries/{IBAN}?customerNumber={customerNumber}
curl -X DELETE https://api.twikey.com/creditor/transfers/beneficiaries/BE16645348971174?customerNumber=123 \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/transfers/beneficiaries/BE16645348971174?customerNumber=123");
curl_setopt($ch, CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, 'Authorization : $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/transfers/beneficiaries/BE16645348971174?customerNumber=123',
method: 'DELETE',
headers: {
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void removeTransaction(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/transfers/beneficiaries/BE16645348971174?customerNumber=123")
.delete(null)
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; // gathered through logIn
public void removeTransaction(){
RestClient client = new RestClient(host + "/creditor/transfers/beneficiaries/BE16645348971174?customerNumber=123");
RestRequest request = new RestRequest(Method.DELETE);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
HTTP Response
Code | Description |
---|---|
204 | the request has succeeded, no content |
400 | bad request |
404 | the entity was not found |
Error codes
Code | Description |
---|---|
err_not_found | No such beneficary |
err_invalid_iban | No beneficary found for that IBAN |
err_fail_refund | Beneficiary account is disabled |
Issuers
Some requests require to pass a BIC code to identify the bank. With this request you can fetch the banks connected to Emachtiging, iDIN
Fetch connected banks
Return the banks that are currently connected to iDIN and Emachtiging. For Emachtiging CORE banks are returned by default. A 'type' parameter can be passed to return B2B supported banks.
HTTP Request
GET /creditor/issuers/<method>
curl https://api.twikey.com/creditor/issuers/emachtiging \
-H 'authorization: authorization'
$host = "https://api.twikey.com";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/issuers/emachtiging");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/issuers/emachtiging',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String authorisation = null; //collected through logIn
public void createCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
Request request = new Request.Builder()
.url(host + "/creditor/issuers/emachtiging")
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com"
authorisation = null; // collected through logIn
public void createCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/issuers/emachtiging");
RestRequest request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
}
}
Sample response:
[
{
"Bic": "ABNANL2A",
"Name": "ABN AMRO"
},
{
"Bic": "ASNBNL21",
"Name": "ASN"
}
]
Method
Replace
- emachtiging
- idin
Query parameters
Name | Description | Required | Type |
---|---|---|---|
type | only for method emachtiging. 'core' (default) or 'b2b' | No | string |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
400 | bad request |
Customer
Update a customer
Update a customer via this request. It is not possible create a new customer or to merge customers using this request.
curl PATCH 'https://api.twikey.com/creditor/customer/{customerNumber}?
firstname=John
&lastname=Doe
&address=Highstreet 66
&city=Gent
&zip=9000
&country=BE
&l=nl
&email=mycustomer@example.com
&mobile=+32488995577
&ct=2223
&customerNumber=newCustomerNumber123' \
--h 'Authorization: .....'
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/customer/{customerNumber}?firstname=John&lastname=Doe&address=Highstreet%2066&city=Gent&zip=9000&l=NL&email=mycustomer@example.com&mobile=+32488995577&ct=2223&customerNumber=newCustomerNumber123',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => array(
'Authorization: ....'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
myHeaders.append("Authorization", "....");
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'PATCH',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/customer/{customerNumber}?firstname=John&lastname=Doe&address=Highstreet 66&city=Gent&zip=9000&lang=NL&email=mycustomer@example.com&mobile=+32488995577&ct=2223&ref=newCustomerNumber123", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/customer/{customerNumber}?firstName=John&lastname=Doe&address=Highstreet 66&city=Gent&zip=9000&l=NL&email=mycustomer@example.com&mobile=+32488995577&ct=2223&customerNumber=newCustomerNumber123")
.method("PATCH", body)
.addHeader("Authorization", ".....")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/customer/11233?firstname=John&lastname=Doe&address=Highstreet 66&city=Gent&zip=9000&l=NL&email=mycustomer@example.com&mobile=+32488995577&ct=2223&customerNumber=newCustomerNumber123");
client.Timeout = -1;
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", ".....");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
HTTP Request
PATCH https://api.twikey.com/creditor/customer/{customerNumber}
Query Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
ct | Default template | No | integer | 4 |
l | Language (en/fr/nl/de/pt/es/it) | No | string | 2 |
customerNumber | Update the customer number | No | string | 50 |
Email of the customer | No | string | 70 | |
lastname | Lastname of the customer | No | string | 50 |
firstname | Firstname of the customer | No | string | 50 |
mobile | Mobile number required for sms (International format 0032499123445) | No | string | 50 |
address | Address (street + number) | No | string | 70 |
city | City of customer | No | string | 50 |
zip | Zipcode of customer | No | string | 50 |
country | ISO format | No | string | 2 |
companyName | The company name (if customer is company) | No | string | 140 |
coc | The enterprise number. Only updated if the company name is also changed | No | string | 50 |
HTTP Response
Code | Description |
---|---|
204 | Request has succeeded |
400 | Request failed |
Error codes
Code | Description |
---|---|
err_invalid_params | Invalid parameters |
err_invalid_country | Country code invalid |
err_invalid_email | Email invalid |
err_invalid_lang | Language code invalid |
err_debtor_not_found | customer not found |
err_duplicate_ref | the customer number already exist |
Other
Rate limits
As we have a far better alternative to querying a single object (feed) sometimes it's necessary to fetch the info about a particular item. Even though it might be true at that particular time. But to ensure these are one-offs and that you don't implement something that won't scale we added rate limits to those requests.
For rate-limited calls we included the response headers in both the request and our documentation:
Header | Description |
---|---|
X-Rate-Limit-Remaining | The number of request remaining for each minute |
X-Rate-Limit-Retry-After-Seconds | Returned once the rate limit is reached. Time (in seconds) before a new request can be made |
Blacklist account
When there is (suspected) abuse or another valid reason, you can add bank accounts to a blacklist. Bank accounts listed can't be used to register new mandates, existing mandates registered with this iban can be suspended or cancelled. The option to notify the customer will trigger the standard email informing the customer that their mandate was suspended, reactivated or cancelled.
Blacklisting is available in the Enterprise subscription.
When adding a bank account to the blacklist a parameter can be passed to change the state of all the mandates registered with this iban. "passive": The mandate is suspended. "cancel": The mandate is cancelled.
curl -X POST 'https://api.twikey.com/creditor/ibanblacklist' \
--h 'Authorization: {{authorization}}' \
--d' rsn=IBAN blacklisted' \
--d 'iban=BE123456789123456' \
--d 'notify=false' \
--d 'state=passive' \
response
"iban": "BE123456789123456",
"rsn": "IBAN blacklisted",
"state": "passive",
"updatedMandates": [
{
"mndtId": "MNDT123"
}
]
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/ibanblacklist',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'rsn=IBAN%20blacklisted&iban=BE123456789123456¬ify=false&state=passive',
CURLOPT_HTTPHEADER => array(
'Authorization: $authorization'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
HTTP Request
POST https://api.twikey.com/creditor/ibanblacklist
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
iban | bank account to add to the blacklist | Yes | string |
rsn | reason | Yes | string |
state | update all mandates state registered with this iban (passive, cancel) | No | string |
notify | notify the customer by email | No | boolean |
HTTP Response
Code | Description |
---|---|
200 | Request succeeded |
400 | Invalid or missing parameter(s) |
Error codes
Code | Description |
---|---|
err_provide_reason | Reason is missing |
err_invalid_params | invalid or missing parameter |
List blacklisted accounts
Retrieve a list of all the bank accounts currently blacklisted.
curl GET 'https://api.twikey.com/creditor/ibanblacklist' \
--h 'Authorization: {{authorization}}'
Sample response:
[
{
"iban": "BE123456789123456",
"rsn": "IBAN blacklisted"
},
{
"iban": "BE33445566889988",
"rsn": "Suspected abuse"
}
]
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/ibanblacklist',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => array(
'Authorization: $authorization'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
var authorization = '.......';
myHeaders.append("Authorization", authorization);
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/ibanblacklist", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/ibanblacklist")
.method("GET", null)
.addHeader("Authorization", ".......")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/ibanblacklist");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "........");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
HTTP Request
GET https://api.twikey.com/creditor/ibanblacklist
HTTP Response
Code | Description |
---|---|
200 | Request succeeded |
Remove blacklisted account
Remove prior added accounts from the blacklist. Suspended mandates can be reactivated using this request.
curl -X DELETE 'https://api.twikey.com/creditor/ibanblacklist' \
--h 'Authorization: {{authorization}}' \
--d 'iban=BE123456789123456' \
--d 'state=active' \
--d 'notify=false'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twikey.com/creditor/ibanblacklist?iban=BE123456789123456&state=active¬ify=false',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => array(
'Authorization: $authorization'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
var myHeaders = new Headers();
myHeaders.append("Authorization", "......");
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'DELETE',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.twikey.com/creditor/ibanblacklist?iban=BE123456789123456&state=active¬ify=false", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.twikey.com/creditor/ibanblacklist?iban=BE123456789123456&state=active¬ify=false")
.method("DELETE", body)
.addHeader("Authorization", "......")
.build();
Response response = client.newCall(request).execute();
var client = new RestClient("https://api.twikey.com/creditor/ibanblacklist?iban=BE123456789123456&state=active¬ify=false");
client.Timeout = -1;
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", "......");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Sample response:
{
"iban": "BE123456789123456",
"state": "active",
"updatedMandates": [
{
"mndtId": "MNDT123"
}
]
}
HTTP Request
DELETE https://api.twikey.com/creditor/ibanblacklist
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
iban | bank account to remove from the blacklist | Yes | string |
state | reactivate all suspended mandates using this iban (active) | No | string |
notify | notify the customer by email | No | boolean |
HTTP Response
Code | Description |
---|---|
200 | Request succeeded |
400 | Invalid or missing parameter(s) |
Error codes
Code | Description |
---|---|
err_invalid_params | invalid or missing parameter |
Account reporting
Get account reporting, this endpoint allows you to retrieve a stream of items listed on your account as soon as they're made available from the bank.
Returns all reporting since the last call.
HTTP Request
GET /creditor/reporting
curl -X GET https://api.twikey.com/creditor/reporting \
-H 'authorization: **authorization**'
$host = "https://api.twikey.com";
$authorization = null; // collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/reporting");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, // collected through login
options = {
host: host,
port: '443',
path: '/creditor/reporting',
method: 'GET',
headers: {
'Authorization': authorization
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void updateTransaction(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(host + "/creditor/reporting")
.get()
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; // collected through login
public void updateTransaction(){
RestClient client = new RestClient(host + "/creditor/reporting");
RestRequest request = new RestRequest(Method.PUT);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("Authorization", authorization);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"Statements": [
{
"id": 146406841,
"type": "PMNT/RCDT/ESCT",
"date": "2018-11-20",
"amount": 1258,
"iban": "BE01234567890123",
"bic": "GEBABEBB",
"msg": "Testing",
"mndtId": "TST123",
"party": "Koen BVBA"
},
{
"id": 262017,
"type": "PMNT/RDDT/PRDD",
"date": "2022-01-19",
"amount": -2500,
"iban": "BE01234567890234",
"bic": "GKCCBEBB",
"msg": "order 1235",
"meta": {
"rc": "AM04",
"e2e": "2265B-20220119134125753882-0"
},
"party": "Wouters Doe"
}
]
}
Response Parameters
Parameter | Description |
---|---|
amount | amount in cents [*1] |
meta.rc | Error code in case of a failed collection of the payment |
meta.e2e | Bank reference |
party | Name of the customer |
[*1]: When the collection failed the amount is negative
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Retrieve Identification url
Identification can be used to identify a customer using a third party (bank, government, ..). When using this request, a url to their services is generated that can be used by the customer. A template of type 'IDENT' is requested and a specific signing method. Currently supported signing methods are iDIN (Netherlands) and itsme (Belgium).
Retrieve direct link from the bank
HTTP Request
POST /creditor/ident
curl https://api..twikey.com/creditor/ident \
-H 'authorization: authorization' \
-d 'ct=**ct_id**'
$host = "https://api.twikey.com";
$ct = "**ct_id**";
$authorisation = null; //collected through login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/ident");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "ct=$ct&bic=ABNANL2A");
curl_setOpt($ch, CURLOPT_HTTPHEADER,"authorization: $authorization");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
ct= "**ct_id**",
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through login
options = {
host: host,
port: '443',
path: '/creditor/ident',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"ct": ct,
"bic": 'ABNANL2A'
}
};
var req = https.request(options, function (res) {
console.log("response: " + res)
});
public class TwikeyApi{
private String host = "https://api.twikey.com";
private String ct = "**ct_id**";
private String authorisation = null; //collected through logIn
public void completeCreditTransfer(){
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody formBody = new FormBody.Builder()
.add("ct", ct)
.add("bic","ABNANL2A")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/ident")
.post(formBody)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorisation)
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();
}
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
ct = "**ct_id**",
authorisation = null; // collected through logIn
public void completeCreditTransfer(){
RestClient client = new RestClient(host + "/creditor/ident");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", authorisation);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter(
"application/x-www-form-urlencoded",
"ct=" + ct,
"bic=ABNANL2A",
ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Query parameters
Name | Description | Required | Type |
---|---|---|---|
ct | Template containing the originating account | Yes | string |
bic | BIC Code of the bank to retrieve a url for (Idin only) | Yes | string |
method | itsme or idin | Yes | string |
l | Language | No | string |
HTTP Response
Code | Description |
---|---|
200 | The request has succeeded |
Error Codes
Code | Description |
---|---|
err_bank_t_o | Time out at the bank |
err_idin_fail | Failure at the bank |
Transaction reservations
Validates if a transaction is eligible for creation and returns a reservation code that can be used to create the actual transaction. The transaction is evaluated against your configured risk rules.
To create a reservation:
- You can use the same parameters as in the actual transaction but an extra 'reservation=true' parameter is required.
Name | Description | Required | Type | Max. |
---|---|---|---|---|
reservation | Indicates this is a reservation (should contain true) | Yes | boolean | 4 |
reservationMinimum | Optional value, which if passed allows Twikey to reserve the highest amount lower than the amount but higher than this value | No | decimal | |
reservationExpiration | Optional date after which this reservation automatically is voided | No | date |
- The default reservation code is valid for 24h
To create a transaction for the reservation:
- Add the header
X-RESERVATION
with the reservation code as value - the amount can be equal or less as the reserved amount
- A response 200 is received: the request succeeded
HTTP Request
POST /creditor/reservation
curl -X POST https://api.twikey.com/creditor/reservation \
-H 'authorization: **authorization**'\
-d 'reservation=true' \
-d 'mndtId=mndtId123' \
-d 'message=Monthly payment' \
-d 'amount=10'
Successful response (status=200):
{
"id":"2e708d7a-a4bf-4535-aea2-3ab24c179519",
"mndtId":"MYMANDATE123",
"reservedAmount":50.00,
"expires":"2021-12-07T22:42:47Z"
}
Or when a risk rule was hit (status=400):
{
"code": "err_billing_overdrawn",
"message": "Maximum amount reached",
"over_amount": "50.00",
"rule_amount": "400.00",
"rule": "Limit400euro",
"tx_amount": "125.00"
}
When a new transaction can't be created due to configured risk rules, the details are returned in the response.
Code | Description |
---|---|
err_billing_overdrawn | Maximum amount/number of transactions reached according to risk rules |
rule_amount | Maximum amount/number of transactions defined in the risk rule |
over_amount | Amount/number of transactions exceeding the risk rule |
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_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_not_authorised | Reservation already exist for this mandate |
Delete a reservation
To delete a reservation you have two methods.
- Use this endpoint to first delete the reservation and then create a new one
- Create a new reservation for the same mandate and pass the parameter
force=true
. This wil delete the old reservation and create a new one in the same request.
HTTP Request
DELETE /creditor/reservation
curl -X POST https://api.twikey.com/creditor/reservation \
-H 'authorization: **authorization**'\
-H 'X-RESERVATION: **reservation id**'
HTTP Response
Code | Description |
---|---|
204 | 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_not_found | Reservation ID not found |
Get payment methods
Retrieve the payment methods activated on a profile under 'Invoices' > 'Payment methods' via this request.
HTTP Request
GET /creditor/payment/methods
Query Parameters
Name | Description | Required | Type |
---|---|---|---|
ct | profile id | Yes | number |
curl -X GET https://api.twikey.com/creditor/payment/methods?ct=1234 \
-H 'authorization: **authorization**'\
Successful response (status=200):
[
"bancontact",
"maestro",
"mastercard",
"visa"
]
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_such_ct | Profile not found |
IBAN-Name Check
The IBAN-Name Check allows you to verify account details registered at the bank of origin. When a response is returned, it is wat is registered at the bank-side, not in your Twikey environment.
Two integrations are possible, one for the Netherlands and one for Italy.
HTTP Request
POST /creditor/ibancheck
curl -X POST https://api.twikey.com/creditor/ibancheck \
-H 'authorization: **authorization**'\
-d 'name=Doortje Doorzon' \
-d 'iban=NL51ABNA0577013939'
$host = "https://api.twikey.com";
$authorization = null; /collected through logIn
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$host/creditor/ibancheck");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"name=Doortje Doorzon"
."&iban=NL51ABNA0577013939");
$server_output = curl_exec ($ch);
curl_close ($ch);
var https = require('https'),
querystring = require('querystring'),
host = "api.twikey.com",
authorization = null, //collected through logIn
options = {
host: host,
port: '443',
path: '/creditor/ibancheck',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authorization
},
body :{
'name' : 'Doortje Doorzon',
'iban': 'NL51ABNA0577013939'
}
};
var req = https.request(options, function (res) {
console.log("result : ",result);
});
public class TwikeyAPI {
private String host = "https://api.twikey.com",
authorization = null; //collected through logIn
public void ibanNameCheck(){
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("name", "Doortje Doorzon")
.add("iban", "NL51ABNA0577013939")
.build();
Request request = new Request.Builder()
.url(host + "/creditor/ibancheck")
.post(body)
.addHeader("content-type", "application/x-www-form-urlencoded")
.addHeader("authorization", authorization)
.build();
Response response = client.newCall(request).execute();
};
}
public class TwikeyAPI {
private String host ="https://api.twikey.com",
authorization =null; //collected through logIn
public void ibanNameCheck(){
RestClient client = new RestClient(host + "/creditor/ibancheck");
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", authorization);
request.AddParameter("application/x-www-form-urlencoded",
"name=Doortje Doorzon" +
"iban=NL51ABNA0577013939"
, ParameterType.RequestBody
);
IRestResponse response = client.Execute(request);
}
}
Sample response:
{
"iban": {
"found": true,
"valid": true,
"blackListed": false,
"nameMatching": true
},
"name": {
"valid": true,
"suggestion": "some suggestion"
},
"account": {
"foreign": false,
"countryCode": "NL",
"active": true,
"holderType": "NP",
"holderResidenceMunicipality": "Amsterdam",
"holderOfficialName": "",
"holdersNumber": 1,
"holderJointAccount": false
}
}
IBAN Name Check Netherlands
Request Parameters
Name | Description | Required | Type | Max. |
---|---|---|---|---|
name | Name of the account holder | Yes | string | 70 |
iban | Account number | Yes | string | 34 |
flavour | it or nl integration to use, by default NL is used |
No | string | 2 |
Response Parameters
Name | Description | Type |
---|---|---|
iban.found | True if the IBAN is found in the registry | boolean |
iban.valid | True if the specified IBAN has a valid format | boolean |
iban.blackListed | True if the iban is blacklisted | boolean |
iban.nameMatching | True if the name matches the name of the account holder | boolean |
name.valid | The name registered in the system (not the one provided) is valid [*1]. | boolean |
name.suggestion | When the provided name doesn't match the account holders name or type [*2]. | string |
account.foreign | True if the account is Dutch | boolean |
account.countryCode | Country code of the account (ISO 2 characters) | string |
account.active | True if the account is active | boolean |
account.holderType | Type of account holder. [*3] | string |
account.holderResidenceMunicipality | City of the account holder | string |
account.holderOfficialName | Official name of the account holder | string |
account.holdersNumber | Number of account holders for this account | number |
account.holderJointAccount | True if it is a joint account | boolean |
[*1] The name registered in the system (not the one provided) is valid
- True (valid): The name is between 3 and 70 characters long.
- False (invalid): if the name is shorter than 3/longer than 70 characters or if only first name is entered.
[*2] When the provided name doesn't match the account holders name or type
- When the provided name does not completely match the account holders name, a suggestion is returned. E.g.: in case of a mistype.
- When the account belongs to an organization, the legal name of the organization is provided.
[*3] Type of account holder.
- NP: person
- ORG: Organization
- Unknown: account type is unknown
IBAN Name Check Italy
This checks that the IBAN and the VAT number or fiscal code match.
Request Parameters
Both codfis
and vatno
are considered optional but at least one value should be passed when making the request.
When both values are given and both values are not empty strings vatno
will take precedence over codfis
.
Name | Description | Required | Type | Max. |
---|---|---|---|---|
iban | Account number | Yes | string | 34 |
flavour | it or nl integration to use, by default NL is used |
Yes | string | 2 |
codfis | Fiscal code related to the IBAN | No | String | |
vatno | VAT Number related to the IBAN | No | String |
Response Parameters
Name | Description | Type |
---|---|---|
unsupported | check is not supported for specified IBAN | boolean |
valid | IBAN check result is valid | boolean |
blacklisted | the given IBAN is blacklisted in our system | boolean |
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_integration | No integration was configured |
err_invalid_name | The provided name is invalid |
err_err_invalid_iban | The provided account is invalid |
err_fail_integration | An error occurred on the system |
err_missing_attributes | missing required parameters |
Accepted SEPA Characters
Here you can find a list of the accepted SEPA characters that are accepted for transactional endpoints.
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
/ - ? : ( ) . , ' +
Space
Reset the feed
When you need to access previous data because your internal system is out of sync for example,
you can reset it.
The reset can only be done using the interface. To do this:
- Go to your Twikey environment
- Open the 'Settings' and select 'Api'
- Click on 'Reset feed'
A modal will open where you can select a specific feed (Contracts, Transactions, Payment links, ..).
By default 'Completely reset feed' is enabled. This applies only to the selected feed
When you disable this option you can enter a custom date and time to which that feed needs to be reset.
Once done click on 'Apply' and the feed is reset.
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 webhook should not be used to replace the API requests as the API delivers a lot more detailed information and we continue to improve the information available in the api calls. 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 'X-Signature' header. This is a one-way encryption (HMAC256) of the url-decoded posted fields with your secret key (apiToken) converted into a hex encoded string.
Setup the different webhooks in your Twikey Dashboard under 'Settings: Api'. The scope selection determines the webhooks you'll receive (documents, payments, customer updates and other events).
You can test your implementation by using the 'Test' button in your environment (Api settings) which will send the sample payload 'msg=dummytest&type=event' as text.
Asynchronous Processing
When receiving a webhook some may opt to perform some methods first and only return a response to us once the methods are completed. This approach is highly discouraged, especially since it is possible that the response won't be returned and Twikey will consider the receiver as non-responsive. If this was the case Twikey will re-offer the same webhook again which will trigger your logic again.
To ensure a good flow, asynchronous processing is therefor (strongly) recommended.
$apiKey = "**API_KEY**";
$message= urldecode($_SERVER['QUERY_STRING']);
$xsignature= hash_hmac('sha256', $message, $apiKey);
$provided_signature= $_SERVER['HTTP_X_SIGNATURE']; // To verify that both signatures match
echo $xsignature;
Retry mechanism
When a HTTP request is made, but we receive an error code (e.g. 3xx, 4xx, 5xx) the request is marked to be retried. If a status is non-2xx we consider it a temporary issue and will retry as soon as time permits (usually within a couple of seconds). This will however not block other webhooks.
If however we get a timeout or no response at all we consider there is something wrong on your end and we'll block temporarily all webhooks. We'll retry however after 30sec./5min./30min to stop an hour. Once your side becomes responsive again you can retry the failed messages by pushing the retry button in the app. Hence the importance to not try to do your 'real' work during the webhook.
Note that the url to be given in the interface is without parameters since these are filled up depending on the type.
Types
type=contract
curl -H "apiToken=MyToken" https://example.com/callback \
-d type=contract \
-d reason=resumed \
-d mandateNumber=CORE01 \
-d name=<empty> \
-d contractNumber=General terms and conditions \
-d state=SIGNED \
-d event=Update
Triggered when something happens to the mandate. Activation events will be included in the event=Update with the reason being either 'resumed' or 'suspended'. Update events can also contain a specific _Txx code.
- mandateNumber: Mandatenumber
- contractNumber: Contract reference defined on the template under 'General', or a custom reference if assigned one during the creation of the document.
- state:
- When the event is
Print
orSign
the State value is the actual state of the document - Other events: previous state of the document
- When the event is
- name: This parameter is not used anymore and the value is always empty when returned (kept in place for some older configurations).
- event: Name of the event
- reason: Optional reason
Specific codes:
- _T50 Account changed
- _T51 Address changed
- _T52 MandateNumber changed
- _T53 Name changed
- _T54 Email changed
- _T57 Customer changed
- _T00 Attribute updated (any attribute, including `maxAmount` for Emachtiging
type=plan
type=customer
Triggered on customer updates.
Eg. When two customers are merged, you receive a webhook for both customers. If a customer has documents, you'll recieve a webhook with type=contract for every document.
type=payment
type=payment
type payment
type=payment (one-off payment)
amount 45.42
ct 2223
id 186229
ref 123456
status paid
type payment
Triggered when a payment was updated. Webhooks involving payments can have multiple origins. Either it's a one-off payment via a psp in which case this webhook will provide you with the details. In case a whole batch of payments is received (eg. Sdd) then we only send a webhook once and depending on the type of transaction (be it invoice or sdd) you can call the respective feed.
type=dunning
type=dunning
mandateNumber MNDT123
txIds [258802]
type dunning
Triggered when a dunning action was executed for a failed transaction. Option to notify 'webhook' must be activated in the dunning actions.
Extra parameters:
- mandateNumber : Mandatenumber
- txIds : Transaction ID
type=letter
type letter
mailId JJBEA120090000185103100
docType contract
createdOn 2021-11-17 10:58:01.962
sentOn 2021-11-17
delivered true
docNumber WIKLETTER420
Triggered when a letter was delivered or bounced.
Extra parameters:
- mailId : Id returned by the actual post service
- docType : Type of document
- createdOn : When the document was generated
- delivered : When the document was delivered to the post service
- docNumber : Reference to the document (invoice number or mandate number)
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 or named.
- {0} / {{mandateNumber}} : mandate number
- {1} / {{status}} : status (ok = returned without error)
- {2} / {{account}} : iban/bic encrypted wih key privateKey+contract.getMandateNumber()
- {3} / {{s}} : signature over mandate and status separated by a slash (hex encoded)
- {4} / {{token}} : token (optional: if passed in the incoming url)
Some of the exceptional statusses can be (but not limited to):
- ok: document is signed
- cancel: cancel in the signing flow
- fail: signature / authentication failed
- pending: signature still pending (customer printed the document, extra signatures required)
Exit url's can be defined for mandates and invoices as described in How to define an exit URL.
In order to be sure that the return call is coming from Twikey, it's strongly recommended to verify the signature to the exit url. View our sample code snippets to find examples in various languages on how to calculate the signature.
eg. an exit url with the following value
http:///website.com/{0}/{1}/{3}
or
http:///website.com/{{mandateNumber}}/{{status}}/{{s}}
would be expanded to if the end-customer returned without error
http:///website.com/MND123/ok/C9FB0D93...
Test data
In our Beta environment you can simulate specific outcomes.
Transactions outcomes
Every 30 minutes, a job is run to mock feedback from the bank. Transactions sent to the bank then receive a paid or failure state with a specific error code depending on their amount. You can test your failed payments configuration (dunning) using these amounts.
Important: To mimic the same behaviour as in a live environment, the first feedback is always PAID
.
The second time the job runs, the actual feedback is returned.
- 1 to 10 euro: Insufficient funds (soft fail):
- 11 to 20 euro: Customer refuses (hard fail):
- 21 to 32 euro: Failure at bank (other):
- 33 ≥: Paid
Dunning
The grace period in days that you define in your Dunning Steps, are reduced to minutes in BETA to avoid that you need to wait days to verify if your dunning is configured and executed as expected.
Depending on the number of days that you defined, the next dunning step is triggered (1 day = 1 minute).
The shorter the grace period, the quicker that the next step is executed.
B2B Mandate acceptance
Every 30 minutes, a job runs that accepts/rejects a mandate based on the last number of the iban. If the number is even, the B2B mandata will be accepted, otherwise will be rejected. Note: this is not the case for Belfius (BIC = GKCCBEBB), BNP (BIC=GEBABEBB) account numbers.
Please use the below examples for automated feedback testing purposes:
B2B Mandate accepted scenario --> BE16 6453 4897 1174
B2B Mandate rejected scenario --> BE17 3217 8221 4921
Only possible using connected banks
For Dutch B2B signing you can simulate the desired outcome (see below under 'iDeal, Emachtiging and iDIN').
For not-connected banks only the print and mark as signed flows are possible
B2B Automated validation
In BETA the outcome of the transaction to validate the mandate is done based on the IBAN used on the mandate.
IBAN's ending on an even number are accepted after 4 hours to simulate the waiting time in production (circa 5 bank days) before the mandate is set on signed after a successful validation.
IBAN's ending with an uneven number are rejected, this directly when the transaction received final bank feedback (can take up to 1 hour).
in both cases, the first bank feedback is always PAID, only after the second feedback of your batch the actual feedback is returned and the mandate accepted or rejected.
Test accounts
Success: DE43100000000123456780
Failure: DE60740201008441962539
Failure / Success: DE80783500000040982001
These are just example accounts. You can use any not-connected account (that is B2B enabled), the last number defines the behaviour:
- Success: Even numbers
- Failure: Uneven numbers (except for 1)
- Failure first, then Success the next retry: 1
Simulating feedback
In beta a jobs runs about every 30 minutes to simulate feedback of the bank. This needs to run twice to get the final outcome (as the bank also always returns feedback twice in a live environment).
When you test a successful validation: a task is created to run after 4 hours. Once that task runs the mandate is accepted. When you opt to use an IBAN with '00' at the end, the mandate is signed directly after the second job ran.
Possibly you will receive a duplicated webhook for the same mandate due to the task that runs. this is a know issue occurring only in BETA.
Sign methods and data
iDeal, Emachtiging and iDIN
Contact your customer success agent to link your template to a test bank. Provide a template ID and signing method.
When using iDIN to sign or for identification, the customer data is overwritten by default to simulate as if you received the data back from iDIN as it would in a live environment. This can be disabled or customized by your customer success agent.
You can also add a delay for these signing methods. Those will delay the feedback in the back. On the frontend you will directly go to the verification page and not see this. The transaction is then verified on a periodic base.
Other signing methods
eID: with your own personal eID card
Bank cards (Maestro): Any valid card number or 41111 or 4111111111111111
- Expiry month = January: Hard failure
- Expiry month uneven: Failure
- Expiry month even: Success
SMS: When inviting to sign via sms a mock email is sent to the email address configured under Settings -> Company information. In the email there you can open a link. When opened you can:
- Enter 'OK': Success
- Enter any other value: Failure
Letters
When sending invites (documents, invoices, ..) note that no real letters are sent out. A copy of the letter is sent to you (the user performing the action) via email. This way you can verify the content before going live.
WIK letters need to be released by your Customer Success Agent. You then receive a zip file (to the email address configured under Settings -> Company information) containing the PDF's files.
Wire transfer
You can simulate a wire transfer. This enables you to test automatic and manual matching. You need to replace the IBAN with an actual account from one of your bank gateways.
Create a CSV file with following contents:
twikey:BE123456789123
name;iban;bic;msg;amount
John Doe;BE31798258915655;GKCCBEBB;My product;100
Note: the amount is in cent
Upload this file under Reconciliation - 'Upload account info'. Note: the filename must be unique for each upload.
Automatic matching of an invoice:
- The 'msg' need to correspond to the payment information of an existing invoice
- The 'amount' need to correspond with this same invoice
When matched, the invoice is marked as paid.
Webhooks
You can also test our webhooks to ensure they are correctly handled on your end and that the signature is correctly calculated. When activating the webhook via the API settings, no parameters are required for the url. Those are added when we send out the request and can be different depending on the type.
For the full list of types and details you can refer to the Webhook documentation.
Collection Agency (Intrum Only)
After a transaction was sent to the collection agency feedback is received every 15 minutes (or until the case is closed).
When using the Intrum-API integration you can use the following amounts for your transactions to simulate feedback from the agency:
case 1, 2, 3, 4, 5 -> Paid in full
case 6, 7, 8, 9, 10 -> Paid in full with 1 euro incremental payments
case 11 -> Closed not paid with exit BANKRUPTCY
case 12 -> Closed not paid with exit DELETED
case 13 -> Closed not paid with exit LACKOFEVIDENCE
case 14 -> Closed not paid with exit DECEASED
case 15 -> Closed not paid with exit DEBTRELIEF
case 16 -> Closed not paid with exit DISPUTED
case 17 -> Closed not paid with exit COMBINEDCASE
case 18 -> Closed not paid with exit NOTCOLLECTABLE
case 19 -> Closed not paid with exit FRAUD
case 20 -> Closed not paid with exit NOREACTION
anything else -> Partial payment
Payment links
When using the header X-Purpose
in test and you create a QR code from it, this code can not be scanned by bank apps.
Only using our production environment you can create a valid url.
The url itself can still be used in our BETA environment, only when generating a QR from it, this is not valid for bank apps.
Changes
Upcoming Changes
No upcoming changes currently.
Beta Changes
March 2024
- KBC bank accounts are no longer used to automatically simulate B2B mandate acceptance
Before you could automated B2B mandate acceptance using KBC bank accounts, this has now changed. See our Test data documentation to view the IBAN's that can and can't be used for each flow.
- KBC bank accounts are no longer used to automatically simulate B2B mandate acceptance
August 2023
- 02/08 Transactions: Payment feedback
Before a batch of transactions returned the actual outcome (fail/success) based on the amount directly when the automated task (every 5 to 15min) runs. Now, to improve a real production simulation, the first feedback of the batch is always paid, then the second time the automated tasks runs the actual feedback is returned.
- 02/08 Transactions: Payment feedback
Production Changes
December 2024
- The
/creditor/sign
request withmethod:import
for B2B mandates was modified. B2B mandates are now directly signed instead of awaiting bank approval unless you pass the parameterbankSignature: false
in your request
- The
September 2024
- A new endpoint
PATCH subscription
was added to update or move a subscription to a different mandate without creating a new one.
- A new endpoint
August 2024
- A new endpoint
/transaction/query
is now available to do a query search of your transactions starting from a specific transaction ID - IBAN-Name Check: New parameters are now supported to use the Italian integration over API
- A new endpoint
July 2024
- Create payment links: removed method
giropay
as this service was terminated by the banks
- Create payment links: removed method
June 2024
- Mandate invite:
reminderDays
parameter is now ignored when you have automatic reminders enabled on the profile to avoid duplicated reminders
- Mandate invite:
May 2024
- A new endpoint
/creditor/collect/query
is now available to do a query search of your collections - Idempotency-Key: A
409
is now returned when a response can't be returned yet (when doing subsequent requests too soon) - Refunds:
Idempotency-Key
header is now supported when creating new refunds
- A new endpoint
April 2024
- The parameter
mndtId
for the endpointPOST creditor/collect
is removed. - A query parameter
include=seq
is now available for theGET creditor/transfer
request
- The parameter
March 2024
- A new endpoint
GET creditor/mandate/query
to return contracts based on a search query.
- A new endpoint
November 2023
- Create invoice via UBL: New header to pass your own UUID
October 2023
- Updated SSL ciphers to be compliant with latest PCI regulations.
August 2023
- 31/08 Invoice feed: payment method is now available
When retrieving the invoice feed the payment method used to pay the invoice is now retrieved when using the parameterinclude=meta
in your request.
- 31/08 Invoice feed: payment method is now available
July 2023
19/6 Create invoice: Link a credit note to existing invoice
A new parameter is addedrelatedInvoiceNumber
to link your credit note (new invoice with a negative amount) to an existing invoice. This enables you to do a refund based on the payment method of that related invoice.19/6 Action on invoice: Directly refund a credit note
Using our endpoint action on an invoice you can now directly initiate a refund on a credit note.
March 2023
- 11/3 Transaction feed
The feed now includes the additional parameter 'bkamount' which is used when receiving feedback of partial payments from the collection agency. The amount is increased with each partial payment.
- 11/3 Transaction feed
February 2023
- 10/2 Mandate feed
A new include parameter addedinclude=cancelled_mandate
to return additional customer information for cancel events.
The email and customer number are then returned in the feed. - 3/02 Invoice update
Updating invoices to paid which are already paid will return the invoice object instead of an errorerr_invalid_state
.
- 10/2 Mandate feed
January 2023
- 26/01 Idempotency support
For our create transaction, refund and subscriptions request we now support a new headerIdempotency-Key: "your unique reference"
to ensure no duplicated request are made. - 06/01 Mandate feed: subscription events
The mandate feed now includes subscription events. A mandate will be returned in the feed when a subscription is created, suspended, resumed or cancelled.
- 26/01 Idempotency support
December 2022
- 06/12 Accept Header: multiple types
The Accept header for a request will determinate if we return the response in JSON or XML. If you accept multiple types we return JSON only if it is included in the header. - 01/12 Minor update on the way webhooks are retried
A more sensible approach is taken not to block all webhooks if you return a non-2xx. Timeouts and unresponsive endpoints still block future processing - 15/12 Deprecation of old amount _tx* and _pl* parameters in the invite/sign call
More sensible names were given to transactions and subscriptions in the invite/sign call
- 06/12 Accept Header: multiple types
November 2022
- 04/11 Action on a transaction: unsettle
A new action is available for transactions: unsettle
This will mark the transaction as Unsettled.
- 04/11 Action on a transaction: unsettle
October 2022
Short codes from 4 to 8
Shortcodes for document invites went from 4 to 8 characters
Offering more entropy.13/10 Schedule Subscriptions (plans)
Plans are now referred to as Subscriptions.
A new parameter date can be used to schedule standard subscriptions13/10 Payment links: method giropay added
Method 'giropay' is now supported (depending on your psp).
September 2022
23/09 Transaction refund: Credit cards
Refunds for recurring credit card transactions are now possible.
Supported payment providers are: CCV, Multisafepay and Mollie09/09 Mandate feed: include parameters added
To reduce returned objects in each message use the include parameters.
Returning an event id is now also possible.
August 2022
- 08/08 Get payment methods
A new request allows to return the payment methods activated on a template.
- 08/08 Get payment methods
July 2022
- 29/07 Delete invoice
When invoices are not in a state paid or in progress they can be deleted
via this new request. - 07/07 Paymentlinks details and feed: return custom attributes Custom attributes could already be used when creating a paymentlink to use them in your emails. Now they are also returned in the feed and details requests to enhance your follow up.
- 29/07 Delete invoice
June 2022
30/06 Invoice details and feed: new parameter for lastpayment The lastpayment object now returns 'double' (boolean). This indicates a payment was received for the same invoice already in a paid state.
24/06 Create paymentlinks: new methods available New payment methods can be used: Google Pay, Apple Pay and Bank transfer.
13/06 Create invoice: customerNumber as numeric values The customerNumber parameter will accept numeric values too. Before only a string was accepted. When you entered a numeric value before, the parameter was disregarded.
13/06 Upload invoice via UBL: New header When creating a new invoice via UBL, you can now specify a specific template using the new header 'X-TEMPLATE'. As value you can use a template ID or the prefix so the invoice is created on this specific template.
13/06 Credit Transfers: Create a refund without iban From now on, you can create a refund by either passing the iban parameter or the customerNumber provided that a beneficiary account exists.
13/06 Transaction feed and details now include administrative charges When administrative charges are applied to a transaction they will be included in the transaction feed and details as 'admincharge'.
May 2022
20/05 Rate limits: Details request limited per hour A rate limit was already applied per minute for specific details request. For those requests there is now a limit per minute and a global per hour. We introduced two new headers in the response which allow you to retrieve the remaining request left per minute and the time (in seconds) you'll need to wait before you can retry again once the limit was reached. For each of the concerning request you will find the header response in the endpoint description.
20/05 Redirect url for invoices A new parameter can be used when creating a new invoice: "redirectUrl". After payment the customer is redirected to this url. A specific app protocol is required (eg. https). This overrules a global or invoice thank-you page redirect should this be configured on your template
03/05 Customer update: added 'coc' parameter Now you can also update the enterprise number, only when the company name is also changed in the same request.
April 2022
14/04 Get Collection: added 'txref' in the response The response now returns your own transaction reference. This makes it easier to link the transaction to your internal system.
14/04 Mandate feed: translations of the 'rsn'for amendments The mandate feed sometimes included a reason fully translated in text instead of the reason code. Now only when the feedback is from the bank it will be translated, in other cases only the _T5 code is returned.
14/04 Error response for 'register_address_missing' replaced The response 'register_address_missing' is replaced by 'err_missing_params'. Extra information will be included in the response specifying which data is missing (address, city, zip or country). This allows better error handling.
February 2022
23/02 Sign mandate: return a preview of the document The sign method 'paper' now returns the document as base64 in the response. To send the document to a customer via email, you can use the parameter 'sendInvite: true'.
03/02 Reservations: Delete a reservation A new endpoint
DELETE creditor/reservation
added to remove a reservation. Instead of waiting until the reservation time expired, remove it.01/02 Sign mandate: Added 'paper' method A new method added 'paper' to invite the customer via the print flow. Only possible for B2B templates.
01/02 Sign mandate: Send official WIK letter (registered letter) A new method added 'wik'to send a registered letter to a customer. Requires a WIK-template and the parameters 'amount' and 'message' in the request.
January 2022
28/01 Invoices via UBL: New header When uploading invoices via UBL you can now pass the header
X-MANUAL
(boolean). If set on 'true' the invoice is not auto collected via a recurring mechanism.25/01 Invoices: New action type letter Actions on invoices now includes the 'letter' type. Send the invoice via postal letter.
07/01 Payment links: Send registered email A new value added for the parameter 'sendInvite'. The value 'registeredEmail' can be passed to send a registered email (requires integration).
07/01 Create invoice: new parameter 'manual' When creating a new invoice you can pass the parameter 'manual' (boolean). When set on true creates no transaction for the invoice.
07/01 Invoice details: fetch details by invoice number The invoice number can now also be used to fetch invoice details. Before it was only possible via the unique identifier.
December 2021
- 05/12 Allow custom attributes for invoices and payment links You can now use custom attributes defined on your template when creating a new invoice or payment link. The attribute is returned in the detail and feed and can also be used in invitations to pay.
November 2021
- 12/11 Create transaction: additional response added for risk rules When a new transaction cannot be created due to a risk rule, additional information is now included in the response (amount exceeding the risk rule, allowed amount, rule name, ...).
October 2021
29/10 Transaction feed and status: include link The transaction feed and status request can return the payment link url used in dunning. To retrieve this use the 'include=link' parameter. Note: it is only available when the dunning is ongoing.
28/10 Added Update customer request Updating a customer can now be done via the PATCH request 'update customer'.
28/10 Create transaction: 'dryrun' renamed to 'reservation' and improved usage The parameter 'dryrun' was renamed to 'reservation'. The reservation parameter can now be used to not only validate if a transaction can be created, but also create a reservation code for it that is returned in the response header.
18/10 Paymentlinks: Added X-Purpose header Create a new payment link: 'X-Purpose' header was added. You can use this to alter the url for specific use cases.
September 2021
14/09 Fetch connected banks for Dutch issuers: new endpoint New endpoint to fetch connected banks for Dutch issuers.
GET host/creditor/issuers/method
allows to retrieve the connected banks for Emachtiging, iDIN and iDeal.14/09 Create transaction: New parameter 'dryrun' The parameter 'dryrun' (boolean) allows you to do a validation check before creating the transaction. During the dryrun the transaction is evaluated through your risk rules.
August 2021
- 05/08 Company ID is returned in the authorization header
Your company ID is now included in the authorization response header
X-MERCHANT-ID
.
- 05/08 Company ID is returned in the authorization header
Your company ID is now included in the authorization response header
July 2021
28/07 Create invoice: 'title' is generated when empty The value of the 'title' parameter is generated automatically if left empty.
27/07 Mandate invite: added two new parameters '_txd' and '_txr' The parameters enable you to use your own transaction message and reference when the customer signs the mandate using a payment. Those are also used in the verification reconciliation files.
12/07 Mandate invite: can now be sent by letter Invitations can now be send by letter. Two new values are available for the 'sendInvite' parameter. 'letter' or 'letterWithMandate' can be used.
12/07 New Invoice: language handling revised The language of the invoice is now only defined by the language passed in the invoice object. The parameter to use is 'l' or 'locale'
June 2021
21/06 New endpoint for invoices: UBL Import invoices in UBL format.
14/06 New Transaction: 'date' parameter The 'date' parameter is now limited to 10 days in the past and future.
02/06 Reconciliation files: new formats available New formats available: MT940 (Exact Online/Twinfield) and Coda 6 (detail).
May 2021
19/05 Transaction feed: include dunning steps A new parameter 'include=action' returns the dunning steps taken for a transaction.
10/05 Create invoice: updated required parameters for the customer object The customer object parameters for address are not mandatory anymore. The 'customerNumber' and/or 'email' is the minimal requirement now.
07/05 Payment link status and feed: Added more response The meta data now includes the parameter 'active' returning a boolean value about the validity of the link. The parameter 'include=time' was added and returns creation, expiry and last updated.
April 2021
23/04 New Transaction: reference as End To End Identifier The new parameter 'refase2e' can be set on true to use the 'ref' parameter as end to end identifier for the first payment.
19/04 Transaction Feed/Status: include collection Includes the batch identifier in the transaction feed or status.
March 2021
19/03 Transaction feed improvements The feed is now restricted to 100 returned transactions each call. Enhancements were also made for a faster performance.
11/03 Action on transaction: replaced parameter Replaced the deprecated parameter 'transfer' with 'backup'. The parameter is used to send an email with a backup payment method in case of a failed payment.
01/03 Blacklist bank accounts New request added to blacklist bank accounts. Requires Enterprise subscription.
01/03 Invoice details & feed: replaced additional parameter The parameter 'include=paymentdetail' was replaced by 'include=lastpayment'. A more detailed and clear overview of past payments.
February 2021
15/02 Webhooks are now sorted in four catgories Webhooks are now sorted in four categories that can be triggered. Activate or deactivate the webhooks in your Twikey Dashboard under 'Settings: Api'.
05/02 Removed parameter 'mndtId' from the transaction delete request To avoid the risk of deleting transactions in bulk, the parameter was removed.
04/02 New request for collection: import a collection This request can be used to upload a batch for collection.
03/02 Create invoices: return invoice details When the same invoice number already exist, we return the invoice details instead of a duplicate message.
January 2021
- 19/01 New request for transactions: refund a transaction
- 10/01 Allow paymentlinks to be retrieved via transaction (also in feed)
December 2020
- 17/12 Allow archiving of transaction
- 7/12 Ident response should also show mandate number
November 2020
23/11 Removed parameter 'form' in the invite request.
This parameter is not supported anymore. The form can be added in the companyName parameter instead.19/11 New invite or reminder request for invoices: action
Use the action request to send out the initial invitation or a reminder to the customer. Can be done by email or sms.18/11 New parameter for credit transfers: ref
Add a reference to a credit transfer. Enables to create a reference of the transaction between our platform and your (booking) system.10/11 Move a mandate to another customer
Move a mandate to another customer using the mandate update request.09/11 Update or add a customer number
Update or add the customer number when updating mandate details.
July 2020
28/07 New request for invoices: Update invoice
Update title, date, due date and PDF on invoices.28/07 New parameter for payment links: invoice
Create a payment link to pay the total or partial amount of invoices.24/07 New parameter for beneficiary accounts: customerNumber
Ensure the beneficiary account is created on the correct customer.23/07 New parameter for payment links: sms
Send invites for payment links by sms.10/07 New identification method: Itsme
Itsme identification is now possible.
April 2020
29/04 New parameter for payment link feed 'include'
Added possibility of sideloading customers for paymentlinks29/4 Added ct parameter to payment link information
March 2020
23/03 Added parameters and return codes for invoices.
Improved documentation about states of invoices20/03 Renamed error from err_mandate_invalid_state to err_invalid_state
14/03 Improved webhook verification method added (X-Signature)
A newer verification method X-Signature was added to the header. The previous verification method can still be used, but will be dropped end of april
February 2020
- 17/02 Added HTTP request to add comments to a mandate
A new request was added to post a public or private comment on the audit trail of a mandate.
- 17/02 Added HTTP request to add comments to a mandate