NAV Navbar
SDK Version:

Introduction

Welcome to the Citizen documentation.

Discover out how to receive account to account payments and account holder confirmation using our SDKs.

For deeper platform integrations, you can use our API directly.

Getting Started

Decide the payment journeys you need

We have a range of journeys to suit all your customer touchpoints. Use this page to help you understand which journey you need to implement.

Creating your account & adding your business information

To get started with Citizen you first need to create an account.

Your account manager will be on hand to help, but here’s what you can expect: How do I setup a Citizen account?.

Account Validation

Before you can process payments with Citizen, we need to validate your bank account.

See Account Validation for more information.

Once you've setup your account, get in touch to start the account validation process.

Going Live

You can integrate and test in our sandbox (test) environment, which fully replicates the live environment.

Once you are happy with your integration, you can go live simply by changing API keys.

Integrating Pay-Ins

Citizen lets you take pay-ins from customers, directly from their bank account.

The Citizen Pay SDK is a few lines of Javascript, called from your pay-in page.

Adding the JS Pay-In SDK to your page

Javascript: Adding the JS Pay-In SDK

<script src="https://sdk.paywithcitizen.com/v6/sdk/sdk-payin.js" data-api-key="[Your-merchant-public-api-key]"></script>
<script src="https://test-sdk.paywithcitizen.com/v6/sdk/sdk-payin.js" data-api-key="[Your-merchant-public-api-key]"></script>

Javascript: Adding the JS Pay-In SDK programmatically


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://sdk.paywithcitizen.com/v6/sdk/sdk-payin.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}


//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://test-sdk.paywithcitizen.com/v6/sdk/sdk-payin.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}

//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()

The SDK is a Javascript library that you link to from your page.

You can find your public api key in the dashboard in your merchant information page.

Creating the Pay-In Instruction

Java: Creating a Pay-In Session

public class CitizenTransactionDetails implements Serializable {
    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerIdentifier; // The internal id you use on your systems to identity your user
    private String customerEmailAddress;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String description;
    private String customerIpAddress;
    private String customerDeviceOs;
    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-pay-in-endpoint")
public class PayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-pay-in-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPayInTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayInDetails = new CitizenTransactionDetails();
      citizenPayInDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      citizenPayInDetails.setCustomerIdentifier(DB.getCustomerId);//should be the id of the customer's account on the merchant's internal system.
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("This is my reference");
      citizenPayInDetails.setDescription("This is my description");
      citizenPayInDetails.setCustomerIP(details.getIPAddress());
      citizenPayInDetails.setCustomerDeviceOs(details.getOS());
      citizenPayInDetails.setSearchableText(details.getSearchableText());
      citizenPayInDetails.setPayload(details.getPayload());
      citizenPayInDetails.setIsPayloadEncrypted(details.getIsPayloadEncrypted());
      citizenPayInDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<TextNode> payInInitResponse = restTemplate
      .exchange("https://api.paywithcitizen.com/v2/payins/session", HttpMethod.POST,
      new HttpEntity<>(citizenPayInDetails, httpHeaders), TextNode.class);

      String citizenTransactionId = payInInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenTransactionId)); //Return this to your front end
      }
}
public class CitizenTransactionDetails implements Serializable {
    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerEmailAddress;
    private String merchantEmailAddress;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String description;
    private String customerIpAddress;
    private String customerDeviceOs;
    private String searchableText;
    private String payload;
    private boolean payloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-pay-in-endpoint")
public class PayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-pay-in-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPayInTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayInDetails = new CitizenTransactionDetails();
      citizenPayInDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      citizenPayInDetails.setCustomerIdentifier(DB.getCustomerId);//should be the id of the customer's account on the merchant's internal system.
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("This is my reference");
      citizenPayInDetails.setDescription("This is my description");
      citizenPayInDetails.setCustomerIP(details.getIPAddress());
      citizenPayInDetails.setCustomerDeviceOs(details.getOS());
      citizenPayInDetails.setSearchableText(details.getSearchableText());
      citizenPayInDetails.setPayload(details.getPayload());
      citizenPayInDetails.setIsPayloadEncrypted(details.getIsPayloadEncrypted());
      citizenPayInDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<TextNode> payInInitResponse = restTemplate
      .exchange("https://testapi.paywithcitizen.com/v2/payins/session", HttpMethod.POST,
      new HttpEntity<>(citizenPayInDetails, httpHeaders), TextNode.class);

      String citizenTransactionId = payInInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenTransactionId)); //Return this to your front end
      }
}

Setting up a pay-in in your backend is simple, and requires a number of fields set.

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user string Y
customerEmailAddress The email of the paying customer. Only needed for email journey string N
paymentGiro payment type (SEPA or FPS) string Y
amount decimal value of the payment string with valid 2 decimal number format Y
currency currency string with valid ISO currency code Y
reference payment reference shown to the customer. Needs to be alphanumeric and 15 chars max string Y
description description shown to the customer string N
customerIpAddress IP for the paying customer string with valid IPv4 or IPv6 format Y
customerDeviceOs OS for the paying customer string Y
searchableText searchable string string N
payload meta-data of the payment string N
isPayloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl Url that the user is redirected to from the modal on completion of a successful journey string N
failureRedirectUrl Url that the user is redirected to from the modal when closing the modal or on a failed payment string N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N

Starting the Pay-In Journey

Javascript: Starting the Email Pay-In Journey

<script>

// citizenTransactionId - from the previous step
// options - an object that can contain the following fields:
//           1. customerEmailAddress - the email of the user, it used to show the user on which email the email has sent
//           2. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown. 
//           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
//
//           e.g. const options = {
//              customerEmailAddress: "yourCustomerEmail@gmail.com",
//              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
//           }


  let sendPayIn = function (citizenTransactionId, options) {
    window.CITIZEN_PAYIN.startEmailPayInJourney(citizenTransactionId, options);
  }

</script>

Once you have the citizenTransactionId, you can start the pay-in journey modal.

Available Parameters

Parameter Description Type Required
citizenTransactionId string of the transaction ID generated from previous step String Y
options Options that are used for a better user experience.
Available options :
1. customerEmailAddress - the user email that used to create the session, it used to show the user in which email the link is send
2. initiatedCallback - a callback that is triggered when the modal is ready to start the journey
Object N

Starting the Pay-In Journey (Direct Pay-Ins)


You may not want the user to be sent an email to begin their pay-in journey. For this, we offer a way to start the journey on the current page.
This call will redirect the user from the current page to a consent page with the details of the journey.

Javascript: Starting the Pay-In Journey

<script>

// citizenTransactionId - from the previous step

  let sendPayIn = function (citizenTransactionId) {
    window.CITIZEN_PAYIN.startPayInJourney(citizenTransactionId);
   }

</script>

Once you have the citizenTransactionId, you can start the pay-in journey.

Starting the QR code Pay-In Journey


You may not want the user to be sent an email to begin their pay-in journey. For this, we offer a way to show to the user a QR code, where he will be able to scan it and continue the journey on his device.


Javascript: Starting the QR code Pay-In Journey

<script>

// citizenTransactionId - from the previous step

  let sendQRCodePayIn = function (citizenTransactionId) {
    window.CITIZEN_PAYIN.startQRCodePayInJourney(citizenTransactionId);
   }

</script>

Once you have the citizenTransactionId, you can start the pay-in journey.

Customising the Pay-In journey

Citizen offers a range of options to suit your own pay-in screen, and optimise for customer conversion.

Embedded

Embedded View

You can use our customisable widget to easily add Citizen pay-ins directly to the pay-in page. This works best when you have a dedicated pay-ins page with space to set customer expectations (key for conversion)

Modal View

This method takes the customisable widget and puts it in a modal which will open with the explanatory information when the pay button is clicked. This is best when you have less space available on your page.

Window

Window View

The new window option is best for mobile applications. This takes the pay-in experience and hosts it in a new window away from the application when they pay button is clicked. This means there is no need for separate android, iOS or web app integrations.

Custom

Custom View

Of course - you can always start from scratch and build something unique to your site. We’ll always be on hand to help advise on best practices and share learnings from our own user research.

You can customise your own widget here.

Adding Pay-In component to your page

Javascript: Adding the Pay-In component

<script src="https://sdk.paywithcitizen.com/component/v3/citizen-pay-component.js"></script>

// This is where the pay-in component javascript will be loaded.
// In case of standalone, this is where the component will be shown.
<div id="citizen-pay-component"></div>

<script src="https://test-sdk.paywithcitizen.com/component/v3/citizen-pay-component.js"></script>

// This is where the pay-in component javascript will be loaded.
// In case of standalone, this is where the component will be shown.
<div id="citizen-pay-component"></div>

The Pay-In component is a Javascript library that you link to from your page. You will need to add a div to host the component.

Implement Pay-In Component

Javascript: Initialising pay-in component

<script>
  window.citizenAsyncInit = function () {
  window.CITIZEN_PAY_COMPONENT.init({
    paymentDetails: {
      publicApiKey: 'your merchant public api key', 
      amountToShow: 'amount that will shown to the button. If left blank, a text placeholder will replace it',
      currencyToShow: 'currency symbol that will shown to the button. If left blank, a text placeholder will replace it',
     },
    theme: 'screen theme, posible values light, dark. Default value light',
    paymentType: 'the type of the journey, possible values no-email, email. Default value email.',
    mode: 'type of the screen. Possible values standalone, modal, windowed. Default value standalone.'
    style: 'styles that are passed to style the component.'
    })
  };
</script>

You initialise the pay-in component with just your merchant public API key and the transaction id.

You can find your public api key the dashboard in your merchant information page and you can generate the transaction id following this step

The transaction id should be passed into the component via the startPayInJourney function. In case of modal and windowed mode, the function startPayInJourney it will open the modal, or the window that will contain the component.

Parameter Description Type Required
publicApiKey your merchant public api key string Y
paymentType the type of the journey, possible values no-email, email. string Y
amountToShow The amount that will shown to the button. If left blank, a text placeholder will replace it string with valid number format N
currencyToShow The currency symbol that will shown to the button. If left blank, a text placeholder will replace it string with valid ISO currency code N
theme screen theme, posible values light, dark. Default value light string N
mode type of the screen. Possible values standalone, modal and windowed. Default value standalone. string N
style styles that are passed to style the component. object N

Javascript: Setting the citizenTransactionId into component.

<script>
  const openPayInComponent = (citizenTransactionId, customerEmalAddress) => {
    window.CITIZEN_PAY_COMPONENT.startPayInJourney(citizenTransactionId, customerEmalAddress)
  }
</script>

startPayInJourney Parameters

Parameter Description Type Required
citizenTransactionId The transaction id is generated on this step. string Y
customerEmailAddress your customer email, this is for visual only, and it will be used only on email payment journey string N

Possible style parameters

All the sections parameters can have their styles changed for attributes fontFamily, backgroundColor and color. e.g. container: { backgroundColor: "white", color: "black" fontFamily: "Roboto" }

Parameter Description Type
container the container that contains all the elements object
title the title that contains the text "Pay direct from your bank" object
description the text that has the three ticks object
steps the container of the 2 steps icons object
actionText the text above the button object
button the button on the bottom. object

Redirects from banks

Once a user has completed the authorisation of a pay-in within their banking app we allow you to customise where the customer is redirected to. You can assign unique redirects for both a successful and failure journeys.

You can set these redirect in your admin dashboard under your merchant details.

Pay-In Webhooks

You can receive real time pay-in status updates via webhooks.

Configuring the webhooks you receive can be done in your merchant dashboard.

See here for webhook JSON structure & composition.

Viewing Pay-Ins

We provide all customers with access to their own Citizen dashboard where they can see pay-in summaries and search for pay-ins.

You also have the option of displaying this data to your own systems using our Pay-In API.

Going Live with Payments

Providing 5AMLD KYC

Citizen is an authorised payment institute, regulated by the UK Financial Conduct Authority.

We are required to check all of our merchants under the 5AMLD before you can receive payments.

We have made this simple for you; simply follow the instructions here once you have created your entity account.

Testing Payments

You can test payments using the Citizen test bank on our test platform.

Additionally, you can use a live bank to send a 10p donation to charity using our receiving charity bank account.

Updating your receiving bank

Once we have validated your KYC information, you will be required to validate your bank account information. You can do this in two ways:

In both cases we will need to see a copy bank statement which confirms your entity’s legal name prior to go-live.

Integrating Accountholder Verification

Citizen lets you confirm bank account holder information from customers. (this is often referered to as AIS, Account Information Services, in Open Banking).

The Citizen Accountholder Verification SDK is a few lines of Javascript, called from your customer verification/onboarding page.

Adding the JS Accountholder Verification SDK to your page

Javascript: Adding the JS Accountholder Verification SDK

<script src="https://sdk.paywithcitizen.com/v6/sdk/sdk-account-verification.js" data-api-key="[Your-merchant-public-api-key]"></script>
<script src="https://test-sdk.paywithcitizen.com/v6/sdk/sdk-account-verification.js" data-api-key="[Your-merchant-public-api-key]"></script>

Javascript: Adding the JS account verification SDK programmatically


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://sdk.paywithcitizen.com/v6/sdk/sdk-account-verification.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}


//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://test-sdk.paywithcitizen.com/v6/sdk/sdk-account-verification.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}

//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()

The SDK is a Javascript library that you link to from your page

You can find your public api key in the dashboard in your merchant information page.

Initialising the Accountholder Verification SDK

Javascript: Initialising the JS Accountholder Verification SDK

<script>
window.citizenAsyncInit = function () {
  CITIZEN_ACCOUNT_VERIFICATION.init({
      publicApiKey: '[Your-entity-public-api-key]'
    })
  };
</script>

You initialise the Accountholder Verification SDK with your entity public API key:

You can find your public api key the dashboard in your company information page.

Creating the Accountholder Verification Instruction

Java: Creating an Accountholder Verification Session

public class AHCTransactionDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerEmailAddress;
    private String getScopes;
    private String getMerchantId;
    private String searchableText;
    private String payload;
    private boolean payloadEncrypted;
    private List<String> supportedCountries; //(Optional) has to be an array of valid country ISO codes e.g GB, NL 
    private String successRedirectUrl;
    private String failureRedirectUrl; 
    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-ahc-endpoint")
public class AHCEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payment-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenAHCTransaction(AHCTransactionDetails details) {

      AHCTransactionDetails aisDetails = new AHCTransactionDetails();
      aisDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      aisDetails.setCustomerIdentifier(details.getMerchantId);//should be the id of the merchants's account.
      aisDetails.setScopes(details.getScopes());
      aisDetails.setMerchantId(details.getMerchantId());
      aisDetails.setSearchableText(details.getSearchableText());
      aisDetails.setPayload(details.getPayload());
      aisDetails.setPayloadEncrypted(details.getPayloadEncrypted());
      aisDetails.setSupportedCountries(DB.getSupportedCountries());
      aisDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      aisDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> AHCInitResponse = restTemplate
      .exchange("https://api.paywithcitizen.com/v2/account-verification/session", HttpMethod.POST,
      new HttpEntity<>(aisDetails, httpHeaders), TextNode.class);

      String citizenAHCTransactionId = AHCInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenAHCTransactionId)); //Return this to your front end
      }  
}
public class AHCTransactionDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String customerEmailAddress;
    private String getScopes;
    private String getMerchantId;
    private String searchableText;
    private String payload;
    private boolean payloadEncrypted;
    private List<String> supportedCountries; //(Optional) has to be an array of valid country ISO codes e.g GB, NL 
    private String successRedirectUrl;
    private String failureRedirectUrl; 
    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-ahc-endpoint")
public class AHCEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payment-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenAHCTransaction(AHCTransactionDetails details) {

      AHCTransactionDetails aisDetails = new AHCTransactionDetails();
      aisDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      aisDetails.setCustomerIdentifier(details.getMerchantId);//should be the id of the merchants's account.
      aisDetails.setScopes(details.getScopes());
      aisDetails.setMerchantId(details.getMerchantId());
      aisDetails.setSearchableText(details.getSearchableText());
      aisDetails.setPayload(details.getPayload());
      aisDetails.setPayloadEncrypted(details.getPayloadEncrypted());
      aisDetails.setSupportedCountries(DB.getSupportedCountries());
      aisDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      aisDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> AHCInitResponse = restTemplate
      .exchange("https://testapi.paywithcitizen.com/v2/account-verification/session", HttpMethod.POST,
      new HttpEntity<>(aisDetails, httpHeaders), TextNode.class);

      String citizenAHCTransactionId = AHCInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenAHCTransactionId)); //Return this to your front end
      }  
}

You can use scopes to set what information you want from the account:

Parameter Description Type Required
merchantId Your merchant ID. This can be obtained from your admin dashboard string Y
customerIdentifier Used to identify the customer whose bank account details to fetch string Y
customerEmailAddress Email address if using the email journey string N
scopes the account information that the user will provide to you array of 'account_details', 'transactions_details' values Y
searchableText searchable string string N
payload meta-data of the request string N
payloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl Url that the user is redirected to from the modal on completion of a successful journey string N
failureRedirectUrl Url that the user is redirected to from the modal when closing the modal or on a failed request string N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N

Starting the Accountholder Verification Journey (Direct Verification)

Javascript: Starting the Accountholder Verification Journey

<script>
  // transactionId - from the previous step

let sendAHCRequest = function (transactionId) {
    window.CITIZEN_ACCOUNT_VERIFICATION.startAccountVerificationJourney(transactionId);
  }
</script>

Once you have the transaction ID, you can start the accountholder verification journey.
This call will redirect the user from the current page to a consent page with the details of the journey.

Starting the Email Accountholder Verification

Javascript: Starting the Email Account Verification Journey

<script>


  // transactionId - from the previous step
  // options - an object that can contain the following fields:
  //           1. customerEmailAddress - the email of the user, it used to show the user on which email the email has sent
  //           2. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown.
  //           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
  //
  //           e.g. const options = {
  //              customerEmailAddress: "yourCustomerEmail@gmail.com",
  //              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
  //           }

  let sendAHCRequest = function (transactionId, options) {
    window.CITIZEN_ACCOUNT_VERIFICATION.startEmailAccountVerificationJourney(transactionId, options);
   }

</script>

Once you have the transaction ID, you can start the accountholder verification journey modal.

Available Parameters

Parameter Description Type Required
transactionId string of the transaction ID generated from previous step String Y
options Options that are used for a better user experience.
Available options :
1. customerEmailAddress - the user email that used to create the session, it used to show the user in which email the link is send
2. initiatedCallback - a callback that is triggered when the modal is ready to start the journey
Object N

Accountholder Verification Webhooks

You can receive real time payment status updates via webhooks.

Configuring the webhooks you receive can be done in your merchant dashboard.

See here for webhook JSON structure & composition.

Viewing Accountholder Verification

We provide all customers with access to their own Citizen dashboard where they can see account verification.

You also have the option of displaying this data to your own systems using our Accountholder Verification API.

Integrating Verified Pay-Ins

Citizen lets you take pay-ins from customers after they verify the bank account, directly from the verified bank account.

The Citizen Pay SDK is a few lines of Javascript, called from your pay-ins page.

Adding the JS Verified Pay-In SDK to your page

Javascript: Adding the JS Verified Pay-In SDK

<script src="https://sdk.paywithcitizen.com/v6/sdk/sdk-verified-payin.js" data-api-key="[Your-merchant-public-api-key]"></script>
<script src="https://test-sdk.paywithcitizen.com/v6/sdk/sdk-verified-payin.js" data-api-key="[Your-merchant-public-api-key]"></script>

Javascript: Adding the JS Verified Pay-In SDK programmatically


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://sdk.paywithcitizen.com/v6/sdk/sdk-verified-payin.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}


//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://test-sdk.paywithcitizen.com/v6/sdk/sdk-verified-payin.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}

//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()

The SDK is a Javascript library that you link to from your page

You can find your public api key in the dashboard in your merchant information page.

Creating the Verified Pay-In Instruction

Java: Creating a Verified Pay-In Session

public class CitizenTransactionDetails implements Serializable {
    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerIdentifier; // The internal id you use on your systems to identity your user
    private String customerEmailAddress;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String description;
    private String customerIpAddress;
    private String customerDeviceOs;
    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
    private Boolean disableAddingNewBanks;
   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-verified-pay-in-endpoint")
public class PayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-verified-pay-in-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPayInTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayInDetails = new CitizenTransactionDetails();
      citizenPayInDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      citizenPayInDetails.setCustomerIdentifier(DB.getCustomerId);//should be the id of the customer's account on the merchant's internal system.
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("This is my reference");
      citizenPayInDetails.setDescription("This is my description");
      citizenPayInDetails.setCustomerIP(details.getIPAddress());
      citizenPayInDetails.setCustomerDeviceOs(details.getOS());
      citizenPayInDetails.setSearchableText(details.getSearchableText());
      citizenPayInDetails.setPayload(details.getPayload());
      citizenPayInDetails.setIsPayloadEncrypted(details.getIsPayloadEncrypted());
      citizenPayInDetails.setDisableAddingNewBanks(details.getDisableAddingNewBanks());
      citizenPayInDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<TextNode> payInInitResponse = restTemplate
      .exchange("https://api.paywithcitizen.com/v2/payins/verified/session", HttpMethod.POST,
      new HttpEntity<>(citizenPayInDetails, httpHeaders), TextNode.class);

      String citizenTransactionId = payInInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenTransactionId)); //Return this to your front end
      }
}
public class CitizenTransactionDetails implements Serializable {
    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerIdentifier; // The internal id you use on your systems to identity your user
    private String customerEmailAddress;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String description;
    private String customerIpAddress;
    private String customerDeviceOs;
    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
    private Boolean disableAddingNewBanks;
   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-verified-pay-in-endpoint")
public class PayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-verified-pay-in-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPayInTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayInDetails = new CitizenTransactionDetails();
      citizenPayInDetails.setCustomerEmailAddress(DB.getCustomerEmail);//only needed for email journey
      citizenPayInDetails.setCustomerIdentifier(DB.getCustomerId);//should be the id of the customer's account on the merchant's internal system.
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("This is my reference");
      citizenPayInDetails.setDescription("This is my description");
      citizenPayInDetails.setCustomerIP(details.getIPAddress());
      citizenPayInDetails.setCustomerDeviceOs(details.getOS());
      citizenPayInDetails.setSearchableText(details.getSearchableText());
      citizenPayInDetails.setPayload(details.getPayload());
      citizenPayInDetails.setIsPayloadEncrypted(details.getIsPayloadEncrypted());
      citizenPayInDetails.setDisableAddingNewBanks(details.getDisableAddingNewBanks());
      citizenPayInDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<TextNode> payInInitResponse = restTemplate
      .exchange("https://testapi.paywithcitizen.com/v2/payins/verified/session", HttpMethod.POST,
      new HttpEntity<>(citizenPayInDetails, httpHeaders), TextNode.class);

      String citizenTransactionId = payInInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenTransactionId)); //Return this to your front end
      }
}

Setting up a verified pay-in in your backend is simple, and requires a number of fields set.

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user string Y
customerEmailAddress The email of the paying customer. Only needed for email journey string N
paymentGiro payment type (SEPA or FPS) string Y
amount decimal value of the payment string with valid 2 decimal number format Y
currency currency string with valid ISO currency code Y
reference payment reference shown to the customer. Needs to be alphanumeric and 15 chars max string Y
description description shown to the customer string N
customerIpAddress IP for the paying customer string with valid IPv4 or IPv6 format Y
customerDeviceOs OS for the paying customer string Y
searchableText searchable string string N
payload meta-data of the payment string N
isPayloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl It redirect the page that the modal is shown in completion of the journey string N
failureRedirectUrl It redirect the page that the modal is shown in case of closing the modal on failed status string N
disableAddingNewBanks Prevents user from removing existing bank accounts or adding new ones boolean N

Starting the Verified Pay-In Journey with Email

Javascript: Starting the Verified Pay-In Journey with Email

<script>

// citizenTransactionId - from the previous step
// options - an object that can contain the following optional fields:
//           1. customerEmailAddress - the email of the user, it used to show the user on which email the email has sent, otherwise a generic placeholder will shown
//           2. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown. 
//           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
//
//           e.g. const options = {
//              customerEmailAddress: "yourCustomerEmail@gmail.com",
//              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
//           }

  let sendVerifiedPayIn = function (citizenTransactionId, options) {
    window.CITIZEN_VERIFIED_PAYIN.startEmailVerifiedPayInJourney(citizenTransactionId, options);
  }

</script>

Once you have the citizenTransactionId, you can start the verified pay-in journey modal.

Available Parameters

Parameter Description Type Required
citizenTransactionId string of the transaction ID generated from previous step String Y
options Options that are used for a better user experience.
Available options :
1. customerEmailAddress - the user email that used to create the session, it used to show the user in which email the link is send
2. initiatedCallback - a callback that is triggered when the modal is ready to start the journey
Object N

Starting the No Email Verified Pay-In Journey (Direct Verified Pay-Ins)


You may not want the user to be sent an email to begin their verified pay-in journey. For this, we offer a way to start the journey on the current page.

This call will redirect the user from the current page to a consent page with the details of the journey.

Javascript: Starting the Verified Pay-In Journey

<script>

// citizenTransactionId - from the previous step

  let sendVerifiedPayIn = function (citizenTransactionId) {
    window.CITIZEN_VERIFIED_PAYIN.startVerifiedPayInJourney(citizenTransactionId);
   }

</script>

Once you have the citizenTransactionId, you can start the verified pay-in journey.

Starting the QR code Verified Pay-In Journey


You may not want the user to be sent an email to begin their verified pay-in journey. For this, we offer a way to show to the user a QR code, where he will be able to scan it and continue the journey on his device.


Javascript: Starting the QR code Verified Pay-In Journey

<script>

  // citizenTransactionId - from the previous step 
  // options - an object that can contain the following optional fields:
  //           1. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown.
  //           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
  //
  //           e.g. const options = {
  //              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
  //           }

  let sendQrVerifiedPayIn = function (citizenTransactionId) {
  window.CITIZEN_VERIFIED_PAYIN.startQRCodeVerifiedPayInJourney(citizenTransactionId, options);

}

</script>

Once you have the citizenTransactionId, you can start the QR code verified pay-in journey.

Integrating Verified Payouts

Citizen lets you handle the payouts from customers after they verify the bank account, directly to their bank account.

The Citizen Pay SDK is a few lines of Javascript, called from your page.

Adding the JS Verified Payout SDK to your page

Javascript: Adding the JS Verified Payout SDK

<script src="https://sdk.paywithcitizen.com/v6/sdk/sdk-payout.js" data-api-key="[Your-merchant-public-api-key]"></script>
<script src="https://test-sdk.paywithcitizen.com/v6/sdk/sdk-payout.js" data-api-key="[Your-merchant-public-api-key]"></script>

Javascript: Adding the JS Verified Payout SDK programmatically


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://sdk.paywithcitizen.com/v6/sdk/sdk-payout.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}


//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()


function setupCitizenSdk() {

  let scriptTag = document.createElement('script');

  scriptTag.src = "https://test-sdk.paywithcitizen.com/v6/sdk/sdk-payout.js";

  scriptTag.dataset.apiKey = "[Your-merchant-public-api-key]"

  document.body.appendChild(scriptTag)

}

//Then you can call the function on which point you want to load the sdk

setupCitizenSdk()

The SDK is a Javascript library that you link to from your page You can find your public api key in the dashboard in your merchant information page.

Public Key Registration

For our system to be able to verify your payout requests, the public component of the openssl RSA key pair is required. This key is used to sign the requests for the payouts which should be registered on our system through the admin dashboard.

Create a Public/Private Key Pair

Bash: Generate private key

openssl genpkey -algorithm RSA -outform PEM -out private_key.pem -pkeyopt rsa_keygen_bits:2048

A prerequisite for signing any request is to generate an RSA 2048 bit keypair using OpenSSL. By executing the first block, a 2048 bit RSA key named private_key.pem is generated, which will be the private key.

Bash: Generate public key

openssl rsa -in private_key.pem -pubout > public_key.pub

The second step is generating a public key based on the private key. This can be easily done by executing the second block of code, which will generate a public key named public_key.pub

Request Authorisation Token

An authorisation token will be provided by Citizen for the merchant to be able to start the payout journey. When the token is provided to you, you can submit it on the admin dashboard under the payout section.

Embedded View|512x397,10%

Register Google authenticator

Once the token has been entered, you will need to setup Google authenticator to make sure the key cannot be easily changed.

Embedded View|512x397,10%

Register Public component

Once Google Authenticator has been setup, you can register the public key from your RSA key pair that you will use to sign requests.

Embedded View|512x397,10%

No Authorisation token

If there isn't any authorisation token registered for your entity, you need to ask for one from Citizen and try again after receiving it.

Embedded View|100x100

Creating the Verified Payout Instruction

Java: Creating a Verified Payout Session

public class CitizenTransactionDetails implements Serializable {
     // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerEmailAddress;
    private String merchantEmailAddress;
    private String merchantId;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String merchantBankCode;
    private String merchantAccountNumber;
    private String customerIpAddress;
    private String customerDeviceOs;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
    private boolean queue;
    private String heldReason;
    private Boolean disableAddingNewBanks;

   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-payments-endpoint")
public class PaymentEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payment-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPaymentTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayoutDetails = new CitizenTransactionDetails();
      citizenPayoutDetails.setCustomerEmailAddress(DB.getCustomerEmail());//only needed for email journey
      citizenPayoutDetails.setMerchantId(details.getMerchantId());
      citizenPayoutDetails.setAmount(details.getAmount());
      citizenPayoutDetails.setCurrency("GBP");
      citizenPayoutDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayoutDetails.setReference("MyShortReference123");
      citizenPayoutDetails.setCustomerIpAddress(details.getIPAddress());
      citizenPayoutDetails.setCustomerDeviceOs(details.getOS());
      citizenPayoutDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayoutDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayoutDetails.setFailureRedirectUrl("<my-failure-redirect-url>");
      citizenPayoutDetails.setQueue(DB.shouldPayoutQueued());
      citizenPayoutDetails.setHeldReason(DB.getHeldReason());
      citizenPayInDetails.setDisableAddingNewBanks(details.getDisableAddingNewBanks());

      RestTemplate restTemplate = new RestTemplate();

      String citizenVerifiedPayoutUrl = "https://api.paywithcitizen.com/v2/payouts/verified/session";

      HttpHeaders httpHeaders = generateHeadersForSignedRequest(HttpMethod.POST, 
                                                        citizenVerifiedPayoutUrl, 
                                                        citizenPayoutDetails, 
                                                        DB.getPrivateKey);

      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> payoutInitResponse = restTemplate
      .exchange(citizenVerifiedPayoutUrl, HttpMethod.POST,
      new HttpEntity<>(citizenPayoutDetails, httpHeaders), TextNode.class);

      String citizenPayoutTransactionId = payoutInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenPayoutTransactionId)); //Return this to your front end
      }
}

private HttpHeaders generateHeadersForSignedRequest (
     HttpMethod httpMethod, String url, Object requestBody, PrivateKey privateKey)
     throws JsonProcessingException, SignatureException {

     String requestExpiryTime = String.valueOf(Instant.now().getEpochSecond() + 300);

     String textToSign = requestExpiryTime + "|" + httpMethod.name().toUpperCase() + "|" + url + "|";

    if (requestBody != null) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        ObjectWriter objectWriter = objectMapper.writer();
        String requestJson = objectWriter.writeValueAsString(requestBody);
        textToSign += requestJson;
    }

    String signature = generateSignature(textToSign, privateKey);

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.set("Signature", signature);
    httpHeaders.set("Expires-at", requestExpiryTime);

    return httpHeaders;
}

private String generateSignature(String plainText, PrivateKey privateKey)
    throws SignatureException {

    try {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(plainText.getBytes("UTF8"));
        byte[] valueSigned = signature.sign();

        return Base64.getEncoder().encodeToString(valueSigned);
    } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
        throw new SignatureException(e);
    }
}

public class CitizenTransactionDetails implements Serializable {
    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private static final long serialVersionUID = 212345678976543210L;

    private String paymentGiro; //Either FPS or SEPA
    private String customerEmailAddress;
    private String merchantEmailAddress;
    private String merchantId;
    private String amount;
    private String currency; // has to be a valid currency ISO code e.g. USD, EUR, GBP
    private String reference;
    private String merchantBankCode;
    private String merchantAccountNumber;
    private String customerIpAddress;
    private String customerDeviceOs;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL
    private String successRedirectUrl;
    private String failureRedirectUrl;
    private boolean queue;
    private String heldReason;
    private Boolean disableAddingNewBanks;
   //Getters and Setters
}

@RestController
@RequestMapping(value = "my-payments-endpoint")
public class PaymentEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payment-endpoint")
    public Callable<ResponseEntity<TextNode>> createCitizenPaymentTransaction(TransactionDetails details) {

      CitizenTransactionDetails citizenPayoutDetails = new CitizenTransactionDetails();
      citizenPayoutDetails.setCustomerEmailAddress(DB.getCustomerEmail());//only needed for email journey
      citizenPayoutDetails.setMerchantId(details.getMerchantId());
      citizenPayoutDetails.setAmount(details.getAmount());
      citizenPayoutDetails.setCurrency("GBP");
      citizenPayoutDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayoutDetails.setReference("MyShortReference123");
      citizenPayoutDetails.setCustomerIpAddress(details.getIPAddress());
      citizenPayoutDetails.setCustomerDeviceOs(details.getOS());
      citizenPayoutDetails.setSupportedCountries(DB.getSupportedCountries());
      citizenPayoutDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayoutDetails.setFailureRedirectUrl("<my-failure-redirect-url>");
      citizenPayoutDetails.setQueue(DB.shouldPayoutQueued());
      citizenPayoutDetails.setHeldReason(DB.getHeldReason());
      citizenPayInDetails.setDisableAddingNewBanks(details.getDisableAddingNewBanks());

      RestTemplate restTemplate = new RestTemplate();

      String citizenVerifiedPayoutUrl = "https://testapi.paywithcitizen.com/v2/payouts/verified/session";

      HttpHeaders httpHeaders = generateHeadersForSignedRequest(HttpMethod.POST, 
                                                        citizenVerifiedPayoutUrl, 
                                                        citizenPayoutDetails, 
                                                        DB.getPrivateKey);

      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> payoutInitResponse = restTemplate
      .exchange(citizenVerifiedPayoutUrl, HttpMethod.POST,
      new HttpEntity<>(citizenPayoutDetails, httpHeaders), TextNode.class);

      String citizenPayoutTransactionId = payoutInitResponse.getBody().asText();

      return ResponseEntity.ok(new TextNode(citizenPayoutTransactionId)); //Return this to your front end
      }
}


private HttpHeaders generateHeadersForSignedRequest (
     HttpMethod httpMethod, String url, Object requestBody, PrivateKey privateKey)
     throws JsonProcessingException, SignatureException {

     String requestExpiryTime = String.valueOf(Instant.now().getEpochSecond() + 300);

     String textToSign = requestExpiryTime + "|" + httpMethod.name().toUpperCase() + "|" + url + "|";

     if (requestBody != null) {
         ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
         ObjectWriter objectWriter = objectMapper.writer();
         String requestJson = objectWriter.writeValueAsString(requestBody);
         textToSign += requestJson;
     }

     String signature = generateSignature(textToSign, privateKey);

     HttpHeaders httpHeaders = new HttpHeaders();
     httpHeaders.set("Signature", signature);
     httpHeaders.set("Expires-at", requestExpiryTime);

     return httpHeaders;
}


private String generateSignature(String plainText, PrivateKey privateKey)
    throws SignatureException {

    try {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(plainText.getBytes("UTF8"));
        byte[] valueSigned = signature.sign();

        return Base64.getEncoder().encodeToString(valueSigned);
    } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
        throw new SignatureException(e);
    }
}

Setting up a verified payout in your backend is simple, and requires a number of fields set.

Parameter Description Type Required
merchantId Your merchantId. This can be obtained from the admin dashboard string Y
customerIdentifier Used to identify the customer receiving the payout. This can be an identifier internal to your system string Y
customerEmailAddress Customer email address which is used if using the payout journey string N
amount Decimal value of the payout - string with number with 2 decimal places string Y
currency string with valid ISO currency code string Y
paymentGiro payment type (SEPA or FPS) string Y
reference Reference for the payout (max 15 chars) string Y
customerDeviceOs OS for the customer receiving the payout string Y
customerIpAddress IP address of the customer receiving the payout string with valid IPv4 or IPv6 format Y
searchableText searchable string string N
payload meta-data of the payment string N
payloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl Url that the user is redirected to from the modal on completion of a successful payout journey string N
failureRedirectUrl Url that the user is redirected to from the modal on completion of a failed payout journey string N
successBankRedirectUrl Url that the user is redirected to from their bank selection screen on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank selection screen on a cancelled/failed request string N
queue determine if the payout will added to queue and waiting for manual confirmation or submitted right away boolean N
heldReason the reason that a payout added to a queue string N
disableAddingNewBanks Prevents user from removing existing bank accounts or adding new ones boolean N

Payout Request Signature

The request must be signed to authenticate the merchant. The signature is valid until a given expiry time set by the merchant (max 10 minutes from the time the request is made).

The signature plain text is generated by concatenating the request components, each one separated by the '|' delimiter:

expiryTime | httpMethod | url | requestBody

Component Description
expiryTime Integer UNIX timestamp for signature expiry time
httpMethod String HTTP method (GET, POST etc) in upper case
url String URL of the endpoint
requestBody String representation of the JSON request body if present

An example of the signature plain text is as follows

1613639354|POST|https://api.paywithcitizen.com/v1/payouts/verified-payout-session|{"customerEmailAddress":"john.doe@test.com","merchantEmailAddress":"info@trading.com","merchantInternalId":"john.doe","currency":"GBP","paymentGiro":"FPS","merchantBankCode":"608384","merchantAccountNumber":"40027188","amount":"5.50","reference":"Monthly pay out."}
1613639354|POST|https://testapi.paywithcitizen.com/v1/payouts/verified-payout-session|{"customerEmailAddress":"john.doe@test.com","merchantEmailAddress":"info@trading.com","merchantInternalId":"john.doe","currency":"GBP","paymentGiro":"FPS","merchantBankCode":"608384","merchantAccountNumber":"40027188","amount":"5.50","reference":"Monthly pay out."}

The signature plain text is signed by an RSA 2048 bit key, the public part of which will have been registered on the Citizen service in advance. It is base 64 encoded and set in a header.

Starting the Verified Payout Journey (Direct Verified Payouts)

Javascript: Starting the Verified Payout Journey

<script>

  // transactionId - from the previous step


  let sendVerifiedPayout = function (transactionId) {
    window.CITIZEN_PAYOUT.startVerifiedPayoutJourney(transactionId);
  }

</script>

Once you have the transaction ID, you can start the verified payout journey.


This call will redirect the user from the current page to a consent page with the details of the journey.

Starting the Email Verified Payout Journey

Javascript: Starting the Email Verified Payout Journey

<script>

  // transactionId - from the previous step
  // options - an object that can contain the following optional fields:
  //           1. customerEmailAddress - the email of the user, it used to show the user on which email the email has sent, otherwise a generic placeholder will shown
  //           2. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown.
  //           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
  //
  //           e.g. const options = {
  //              customerEmailAddress: "yourCustomerEmail@gmail.com",
  //              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
  //            }

  let sendVerifiedPayout = function (transactionId, options) {
    window.CITIZEN_PAYOUT.startEmailVerifiedPayoutJourney(transactionId, options);
   }

</script>

Once you have the transaction ID, you can start the verified payout journey modal.

Available Parameters

Parameter Description Type Required
transactionId string of the transaction ID generated from previous step String Y
options Options that are used for a better user experience.
Available options :
1. customerEmailAddress - the user email that used to create the session, it used to show the user in which email the link is send
2. initiatedCallback - a callback that is triggered when the modal is ready to start the journey
Object N

Starting the QR code Verified Payout Journey


You may not want the user to be sent an email to begin their verified payout journey. For this, we offer a way to show to the user a QR code, where he will be able to scan it and continue the journey on his device.


Javascript: Starting the QR code Verified Payout Journey

<script>

  // citizenTransactionId - from the previous step 
  // options - an object that can contain the following optional fields:
  //           1. initiatedCallback - a callback that is triggered when the background work is done and the modal is shown.
  //           Used mostly to handle the loading state of the host page. If non is passed, the loading state is handled from the modal
  //
  //           e.g. const options = {
  //              initiatedCallback: function(){console.log("This function called when the modal is about to open")}
  //           }

  let sendQrVerifiedPayIn = function (citizenTransactionId) {
  window.CITIZEN_PAYOUT.startQRCodeVerifiedPayoutJourney(citizenTransactionId, options);

}

</script>

Once you have the citizenTransactionId, you can start the QR code verified payout journey.

Hosted Journeys

Hosted journeys are a way for you to make use of SDK without having to incorporate it into your site. You can just generate a link to a page that we host and then redirect your user to. This way you do not need to worry about how to display the correct details or integrating/updating our SDK

Pay-Ins

Url

https://api.paywithcitizen.com/v2/payins/hosted-payin/generate-link
https://testapi.paywithcitizen.com/v2/payins/hosted-payin/generate-link

Use the following to make use of our standard pay-in journey.

To generate the link you will need to call the following endpoint from your backend as it will use your merchant private api key

Java: Generating Hosted Pay-In Link

public class HostedPayInDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;

    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-payIn-endpoint")
public class HostedPayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payin-endpoint")
    public ResponseEntity<*> createCitizenHostedPayIn(HostedPayInDetails details) {

      HostedPayInDetails citizenPayInDetails = new HostedPayInDetails();
      citizenPayInDetails.setCustomerIdentifier("info@company.com");
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("MyPaymentReference123");
      citizenPayInDetails.setDescription("This is my description for the payment");
      citizenPayInDetails.setJourneyType("HOSTED_PAYMENT");
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://api.paywithcitizen.com/v2/payins/hosted-payin/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayInDetails, httpHeaders), String.class);

      String hostedPayInLink = HostedPayInLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayInLink); //Return this to your front end
      }  
}
public class HostedPayInDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;

    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-payin-endpoint")
public class HostedPayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payin-endpoint")
    public ResponseEntity<*> createCitizenHostedPayIn(HostedPayInDetails details) {

      HostedPayInDetails citizenPayInDetails = new HostedPayInDetails();
      citizenPayInDetails.setCustomerIdentifier("info@company.com");
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("MyPaymentReference123");
      citizenPayInDetails.setDescription("This is my description for the payment");
      citizenPayInDetails.setJourneyType("HOSTED_PAYMENT");
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://testapi.paywithcitizen.com/v2/payins/hosted-payin/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayInDetails, httpHeaders), String.class);

      String hostedPayInLink = HostedPayInLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayInLink); //Return this to your front end
      }  
}
Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user String Y
paymentGiro either FPS (UK faster payments) or SEPA (EU SEPA Inst) String Y
amount A valid payment amount (2 decimal digit number) String with valid 2 decimal number format Y
currency currency String with valid ISO currency code Y
reference the reference shown to the customer in their bank statement. Needs to be alphanumeric and 15 chars max String Y
journeyType the type of the journey, payin (HOSTED_PAYMENT) or verified payin (HOSTED_VERIFIED_PAYMENT) String Y
description A description that will show up on the hosted page to describe what the pay-in is for. String N
searchableText a string that you can use to search for this pay-in String N
payload a string payload that can take any format e.g. JSON object String N
isPayloadEncrypted A boolean to specify whether you want to encrypt your payload (default: false) Boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl the page redirection for a successful payin String N
failureRedirectUrl the page redirection for a failure payin String N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N

You will then get a URL to the hosted page. It has an expiry of 1 week. Once expired you will need to generate a new hosted pay-in.

Response

https://banks.paywithcitizen.com/hosted?transaction-id=fab54752-ddfe-7d61-40a8-51a49a
https://test-banks.paywithcitizen.com/hosted?transaction-id=fab54752-ddfe-7d61-40a8-51a49a

Verified Pay-Ins

Url

https://api.paywithcitizen.com/v2/payins/hosted-payin/generate-link
https://testapi.paywithcitizen.com/v2/payins/hosted-payin/generate-link

Use the following to make use of our standard pay-in journey.

To generate the link you will need to call the following endpoint from your backend as it will use your merchant private api key

Java: Generating Hosted Verified Pay-In Link

public class HostedPayInDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;

    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-verified-payIn-endpoint")
public class HostedPayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payin-endpoint")
    public ResponseEntity<*> createCitizenHostedPayIn(HostedPayInDetails details) {

      HostedPayInDetails citizenPayInDetails = new HostedPayInDetails();
      citizenPayInDetails.setCustomerIdentifier("info@company.com");
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("MyPaymentReference123");
      citizenPayInDetails.setDescription("This is my description for the payment");
      citizenPayInDetails.setJourneyType("HOSTED_VERIFIED_PAYMENT");
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://api.paywithcitizen.com/v2/payins/hosted-payin/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayInDetails, httpHeaders), String.class);

      String hostedPayInLink = HostedPayInLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayInLink); //Return this to your front end
      }  
}
public class HostedPayInDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;

    private String searchableText;
    private String payload;
    private boolean isPayloadEncrypted;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-verified-payin-endpoint")
public class HostedPayInEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payin-endpoint")
    public ResponseEntity<*> createCitizenHostedPayIn(HostedPayInDetails details) {

      HostedPayInDetails citizenPayInDetails = new HostedPayInDetails();
      citizenPayInDetails.setCustomerIdentifier("info@company.com");
      citizenPayInDetails.setAmount(details.getAmount);
      citizenPayInDetails.setCurrency("GBP");
      citizenPayInDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayInDetails.setReference("MyPaymentReference123");
      citizenPayInDetails.setDescription("This is my description for the payment");
      citizenPayInDetails.setJourneyType("HOSTED_VERIFIED_PAYMENT");
      citizenPayInDetails.setSuccessRedirectUrl("<my-success-redirect-url>");
      citizenPayInDetails.setFailureRedirectUrl("<my-failure-redirect-url>");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://testapi.paywithcitizen.com/v2/payins/hosted-payin/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayInDetails, httpHeaders), String.class);

      String hostedPayInLink = HostedPayInLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayInLink); //Return this to your front end
      }  
}
Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user String Y
paymentGiro either FPS (UK faster payments) or SEPA (EU SEPA Inst) String Y
amount A valid payment amount (2 decimal digit number) String with valid 2 decimal number format Y
currency currency String with valid ISO currency code Y
reference the reference shown to the customer in their bank statement. Needs to be alphanumeric and 15 chars max String Y
journeyType the type of the journey, payin (HOSTED_PAYMENT) or verified payin (HOSTED_VERIFIED_PAYMENT) String Y
description A description that will show up on the hosted page to describe what the pay-in is for. String N
searchableText a string that you can use to search for this pay-in String N
payload a string payload that can take any format e.g. JSON object String N
isPayloadEncrypted A boolean to specify whether you want to encrypt your payload (default: false) Boolean N
supportedCountries filter the bank list in bank selection page array of String country ISO codes N
successRedirectUrl the page redirection for a successful payin String N
failureRedirectUrl the page redirection for a failure payin String N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N

You will then get a URL to the hosted page. It has an expiry of 1 week. Once expired you will need to generate a new hosted pay-in.

Response

https://banks.paywithcitizen.com/hosted/payin?transaction-id=fab54752-ddfe-7d61-40a8-51a49a
https://test-banks.paywithcitizen.com/hosted/payin?transaction-id=fab54752-ddfe-7d61-40a8-51a49a

Verified Pay-Outs

Url

https://api.paywithcitizen.com/v2/payouts/hosted-payout/generate-link 
https://testapi.paywithcitizen.com/v2/payouts/hosted-payout/generate-link

Hosted Payouts feature requires to have completed the steps for integrating a verified payout. In other words, it requires to have created a public/private openSSL RSA 2048 bit keypair, registered your public key in our platform through the admin dashboard and finally use the above private to sign the request. If you have already done it for the payouts it is not necessary to go over the procedure. For more detailed guide on the above, check the signature section.

Use the following to make use of our standard verified pay-out journey. However, it is worth mentioning that hosted-payouts require the request to be signed in order to authenticate the merchant, before receiving the final link.

To generate the link you will need to call the following endpoint from your backend as it will use your merchant private api key along with signing details, such as expiration time and base64 encoded signature.

Headers

Header Description
AuthorizationCitizen Your private API key
Expires-at UNIX timestamp for signature expiration
Signature Base 64 encoded signature
Content-Type 'application/json'

Java: Generating Hosted Pay-Out Link

public class HostedPayOutDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-payOut-endpoint")
public class HostedPayOutEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payout-endpoint")
    public ResponseEntity<*> createCitizenHostedPayOut(HostedPayOutDetails details) {

      HostedPayInDetails citizenPayOutDetails = new HostedPayOutDetails();
      citizenPayOutDetails.setCustomerIdentifier("info@company.com");
      citizenPayOutDetails.setAmount(details.getAmount);
      citizenPayOutDetails.setCurrency("GBP");
      citizenPayOutDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayOutDetails.setReference("MyPaymentReference123");
      citizenPayOutDetails.setDescription("This is my description for the payment");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://api.paywithcitizen.com/v2/payouts/hosted-payout/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayOutDetails, httpHeaders), String.class);

      String hostedPayOutLink = HostedPayOutLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayOutLink); //Return this to your front end
      }  
}
public class HostedPayOutDetails implements Serializable {

    // This is just a Java class that represents the json object that you will need to send to the citizen backend.
    // These are required fields.

    private String customerIdentifier;             
    private String amount;              
    private String currency;
    private PaymentGiro paymentGiro;
    private String reference;
    private String description;
    private List<String> supportedCountries; // has to be an array of valid country ISO codes e.g GB, NL

    //Getters and Setters
}

@RestController
@RequestMapping(value = "my-hosted-payOut-endpoint")
public class HostedPayOutEndpoints {

    //Your backend will then make a request to the Citizen service to create a transaction.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-payout-endpoint")
    public ResponseEntity<*> createCitizenHostedPayOut(HostedPayOutDetails details) {

      HostedPayInDetails citizenPayOutDetails = new HostedPayOutDetails();
      citizenPayOutDetails.setCustomerIdentifier("info@company.com");
      citizenPayOutDetails.setAmount(details.getAmount);
      citizenPayOutDetails.setCurrency("GBP");
      citizenPayOutDetails.setPaymentGiro(PaymentGiro.FPS);
      citizenPayOutDetails.setReference("MyPaymentReference123");
      citizenPayOutDetails.setDescription("This is my description for the payment");

      RestTemplate restTemplate = new RestTemplate();

      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.set("AuthorizationCitizen", [YOUR_MERCHANT_PRIVATE_API_KEY]]);

      ResponseEntity<*> HostedPayInLinkResponse = restTemplate
            .exchange("https://testapi.paywithcitizen.com/v2/payouts/hosted-payout/generate-link", HttpMethod.POST,
                       new HttpEntity<>(citizenPayOutDetails, httpHeaders), String.class);

      String hostedPayOutLink = HostedPayOutLinkResponse.getBody();

      return ResponseEntity.ok(hostedPayOutLink); //Return this to your front end
      }  
}

Body

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user String y
paymentGiro either FPS (UK faster payments) or SEPA (EU SEPA Inst) String Y
amount A valid payment amount (2 decimal digit number) String with valid 2 decimal number format Y
currency currency String with valid ISO currency code Y
reference the reference shown to the customer in their bank statement. Needs to be alphanumeric and 15 chars max String Y
description A description that will show up on the hosted page to describe what the pay-out is for. String N
journeyType the type of the journey (default: HOSTED_PAYOUT) String N
searchableText a string that you can use to search for this pay-out String N
payload a string payload that can take any format e.g. JSON object String N
isPayloadEncrypted A boolean to specify whether you want to encrypt your payload (default: false) Boolean N
isQueued A boolean to specify whether you want to enqueue your payout (default: false) Boolean N
successRedirectUrl the page redirection for a successful payout String N
failureRedirectUrl the page redirection for a failure payout String N
successBankRedirectUrl Url that the user is redirected to from their bank selection page on a successful payout string N
failureBankRedirectUrl Url that the user is redirected to from their bank selection page on a cancelled/failed payout string N

The request must be signed to authenticate the merchant. The signature is valid until a given expiry time set by the merchant (max 10 minutes from the time the request is made).

The signature plain text is generated by concatenating the request components, each one separated by the '|' delimiter:

An example of the signature plain text is as follows

1613639354|POST|https://api.paywithcitizen.com/v2/payouts/hosted-payout/generate-link|{"customerIdentifier":"info@company.com","amount":"1","currency":"GBP","paymentGiro":"FPS","reference":"MyPaymentReference123"}

1613639354|POST|https://testapi.paywithcitizen.com/v2/payouts/hosted-payout/generate-link|{"customerIdentifier":"info@company.com","amount":"1","currency":"GBP","paymentGiro":"FPS","reference":"MyPaymentReference123"}

expiryTime | httpMethod | url | requestBody

Component Description
expiryTime Integer UNIX timestamp for signature expiry time
httpMethod String HTTP method (GET, POST etc) in upper case
url String URL of the endpoint
requestBody String representation of the JSON request body if present

The signature plain text is signed by an RSA 2048 bit key, the public part of which will have been registered on the Citizen service in advance. It is base 64 encoded and set in a header.

Response

https://banks.paywithcitizen.com/hosted/payout?transaction-id=f9ce6a1f-0776-4a1c-d98a-23c22b
https://test-banks.paywithcitizen.com/hosted/payout?transaction-id=f9ce6a1f-0776-4a1c-d98a-23c22b

Response

Given that you have successfully completed the above steps, you will then get a link for Hosted Pay-Out. It has an expiry of 1 week. Once expired you will need to generate a new hosted pay-out.

Pay-In API

Our pay-in API allows you to create/start standard pay-in journeys, hosted pay-in journeys. Our pay-in API also enables you to display pay-in transactions information in your own apps and services.

Create Pay-In Session

Initiate a pay-in session on your backend as you will need to provide your private merchant api key (which you can find in your merchant dashboard).

You will then receive a citizenTransactionId. This will be used to start the pay-in journey.

cURL: Create Pay-In Session

curl https://api.paywithcitizen.com/v2/payins/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: your merchant private api key \
  -d '{
      customerIdentifier: 12345,
      paymentGiro: FPS,
      amount: 1.00,
      currency: GBP,
      reference: your reference,
      description: your description,
      customerIpAddress: 127.0.0.1,
      customerDeviceOs: user OS,
      searchableText: searchable text,
      payload: {"address":"some address"},
      isPayloadEncrypted: true,
      supportedCountries: ['GB'],
      successRedirectUrl: "https://www.example.com/success"
      failureRedirectUrl: "https://www.example.com/failure"
    }'
curl https://testapi.paywithcitizen.com/v2/payins/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: your merchant private api key \
  -d '{
      customerIdentifier: 12345,
      paymentGiro: FPS,
      amount: 1.00,
      currency: GBP,
      reference: your reference,
      description: your description,
      customerIpAddress: 127.0.0.1,
      customerDeviceOs: user OS,
      searchableText: searchable text,
      payload: {"address":"some address"},
      isPayloadEncrypted: true,
      supportedCountries: ['GB'],
      successRedirectUrl: "https://www.example.com/success"
      failureRedirectUrl: "https://www.example.com/failure"
    }'

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user string Y
customerEmailAddress The email of the paying customer. Only needed for email journey string N
paymentGiro payment type (SEPA or FPS) string Y
amount decimal value of the payment string with valid 2 decimal number format Y
currency currency string with valid ISO currency code Y
reference payment reference shown to the customer. Needs to be alphanumeric and 15 chars max string Y
description description shown to the customer string N
customerIpAddress IP for the paying customer string with valid IPv4 or IPv6 format Y
customerDeviceOs OS for the paying customer string Y
searchableText searchable string string N
payload meta-data of the payment string N
isPayloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl It redirect the page that the modal is shown in completion of the journey string N
failureRedirectUrl It redirect the page that the modal is shown in case of closing the modal on failed status string N

Response

The API response is a transaction ID that is required by the front end SDK.

String Response: Create Pay-In Session

<citizenTransactionId>

Get a single pay-in Transaction

Return a specific detailed pay-in transaction.

By Transaction ID

cURL: Get Single Pay-In Transaction

curl https://api.paywithcitizen.com/v2/payins/{transactionId}
 -X GET \ 
 -H AuthorizationCitizen: <your merchant private api key> \
 -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/payins/{transactionId}
 -X GET \ 
 -H AuthorizationCitizen: <your merchant private api key> \
 -H Content-Type: application/json

JSON Response: Get Single Pay-In Transaction

{
  "citizenTransactionId": "<unique id for the pay-in transaction>",
  "merchantId": "<id of merchant>",
  "merchantTradingName": "<trading name of merchant>",
  "customerIdentifier": "<the internal id you use on your systems to identity your user>",
  "searchableText": "<a string that you can use to search for this pay-in>",
  "financialServiceProvider": "<financial Service Provider, e.g. BARCLAYS>",
  "creationDate": "<date of creation>",
  "payload": "<a string payload that can take any format>",
  "isPayloadEncrypted": "<boolean value indicated that the payload is encrypted or not>",
  "version": "<version of api>",
  "journeyType": "<type of journey, e.g. EMAIL/NO_EMAIL>",
  "transactionType": "<type of transaction, e.g. PAYIN>",
  "transactionStatus": "<status of the transaction>",
  "paymentMethod": "e.g. OPEN_BANKING",
  "paymentGiro": "e.g. FPS",
  "customerAccountNumber": "<customer account number/iban *Only for verified pay-In*>",
  "customerBankCode": "<customer sort code/bic *Only for verified pay-In*>",
  "merchantAccountNumber": "<merchant account number/iban>",
  "merchantBankCode": "<merchant sort code/bic>",
  "paymentAmount": "<the amount of the payment>",
  "paymentCurrency": "<the currency of the payment>",
  "reference": "<a reference for the payment>",
  "description": "<a description for the payment>"
}

Headers

Header Description
AuthorizationCitizen Your merchant private api key

Response

Attribute Description
citizenTransactionId unique id for the pay-in transaction
merchantId id of merchant
merchantTradingName trading name of merchant
customerIdentifier the internal id you use on your systems to identity your user
searchableText a string that you can use to search for this pay-in
financialServiceProvider financial Service Provider, e.g. BARCLAYS
creationDate date of creation
payload a string payload that can take any format
isPayloadEncrypted boolean value indicated that the payload is encrypted or not
version version of api
journeyType type of journey, e.g. EMAIL/NO_EMAIL
transactionType type of transaction, e.g. PAYIN
transactionStatus status of the transaction
paymentMethod method of payment, e.g. OPEN_BANKING
paymentGiro paymentGiro, e.g. FPS
customerAccountNumber customer account number/iban
customerBankCode customer sort code/bic
merchantAccountNumber merchant account number/iban
merchantBankCode merchant sort code/bic
paymentAmount the amount of the payment
paymentCurrency the currency of the payment
reference a reference for the payment
description a description for the payment

By reference

cURL: Get Single Pay-In Transaction by reference

curl https://api.paywithcitizen.com/v2/payins/{reference}
 -X GET \ 
 -H AuthorizationCitizen: <your merchant private api key> \
 -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/payins/{reference}
 -X GET \ 
 -H AuthorizationCitizen: <your merchant private api key> \
 -H Content-Type: application/json

JSON Response: Get Single Pay-In Transaction

{
  "citizenTransactionId": "<unique id for the pay-in transaction>",
  "merchantId": "<id of merchant>",
  "merchantTradingName": "<trading name of merchant>",
  "customerIdentifier": "<the internal id you use on your systems to identity your user>",
  "searchableText": "<a string that you can use to search for this pay-in>",
  "financialServiceProvider": "<financial Service Provider, e.g. BARCLAYS>",
  "creationDate": "<date of creation>",
  "payload": "<a string payload that can take any format>",
  "isPayloadEncrypted": "<boolean value indicated that the payload is encrypted or not>",
  "version": "<version of api>",
  "journeyType": "<type of journey, e.g. EMAIL/NO_EMAIL>",
  "transactionType": "<type of transaction, e.g. PAYIN>",
  "transactionStatus": "<status of the transaction>",
  "paymentMethod": "e.g. OPEN_BANKING",
  "paymentGiro": "e.g. FPS",
  "customerAccountNumber": "<customer account number/iban *Only for verified pay-In*>",
  "customerBankCode": "<customer sort code/bic *Only for verified pay-In*>",
  "merchantAccountNumber": "<merchant account number/iban>",
  "merchantBankCode": "<merchant sort code/bic>",
  "paymentAmount": "<the amount of the payment>",
  "paymentCurrency": "<the currency of the payment>",
  "reference": "<a reference for the payment>",
  "description": "<a description for the payment>"
}

Headers

Header Description
AuthorizationCitizen Your merchant private api key

Response

Attribute Description
citizenTransactionId unique id for the pay-in transaction
merchantId id of merchant
merchantTradingName trading name of merchant
customerIdentifier the internal id you use on your systems to identity your user
searchableText a string that you can use to search for this pay-in
financialServiceProvider financial Service Provider, e.g. BARCLAYS
creationDate date of creation
payload a string payload that can take any format
isPayloadEncrypted boolean value indicated that the payload is encrypted or not
version version of api
journeyType type of journey, e.g. EMAIL/NO_EMAIL
transactionType type of transaction, e.g. PAYIN
transactionStatus status of the transaction
paymentMethod method of payment, e.g. OPEN_BANKING
paymentGiro paymentGiro, e.g. FPS
customerAccountNumber customer account number/iban
customerBankCode customer sort code/bic
merchantAccountNumber merchant account number/iban
merchantBankCode merchant sort code/bic
paymentAmount the amount of the payment
paymentCurrency the currency of the payment
reference a reference for the payment
description a description for the payment

Get Pay-In bank unavailable fallback url

When the customer is unable to continue the transaction by using a bank listed in the bank selector, the customer can proceed the transaction by directing to the fallback url, which is set from merchant. This fallback url can be any kind of url, such as merchant's webpage, or other third-party payment methods(e.g. paypal). The citizenTransactionId is included in the pay-in bank unavailable fallback url. Thus, the merchant can keep track of this transaction.

cURL: Get pay-In bank unavailable fallback url

curl https://api.paywithcitizen.com/v2/payins/bank-unavailable-url/{citizenTransactionId} \
  -X GET \
  -H Content-Type: application/json \  
curl https://testapi.paywithcitizen.com/v2/payins/bank-unavailable-url/{citizenTransactionId} \
  -X GET \
  -H Content-Type: application/json \

Headers

Header Description
Content-Type 'application/json'

Response

The API response is a url that has as query param the citizenTransactionId or null if the fallback url hasn't been set.

Attribute Description
bankUnavailableFallbackURL the bank unavailable fallback url
transactionId citizenTransactionId

String Response: bank unavailable fallback url

<bankUnavailableFallbackURL>?transactionId=<citizenTransactionId>

Account Verification API

Our Account Verification API enables you to fetch and verify a user's bank account details via Open Banking from the user's bank. These details may then be used in your own applications.

Create Account Verification Session

The journey is initiated by creating an account verification session. This is done from your backend and uses your company private API key (from your admin dashboard).

cURL: Create Account Verification Session

curl https://api.paywithcitizen.com/v2/account-verification/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <merchant private api key> \
  -d '{
          customerIdentifier=john.doe@test.com, 
          merchantId=your merchant ID, 
          scopes=[account_details]
    }'
curl https://testapi.paywithcitizen.com/v2/account-verification/session  \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <merchant private api key> \
  -d '{
          customerIdentifier=john.doe@test.com,          
          merchantId=your merchant ID,
          scopes=[account_details]
    }'

Headers

Header Description
AuthorizationCitizen Your private API key
Content-Type 'application/json'

Request

Parameter Description Type Required
merchantId Your merchant ID. This can be obtained from your admin dashboard string Y
customerIdentifier Used to identify the customer whose bank account details to fetch string Y
customerEmailAddress Email address if using the email journey string N
customerIpAddress IP for the customer string with valid IPv4 or IPv6 format N
customerDeviceOs OS for the customer string N
scopes the account information that the user will provide to you array of 'account_details', 'transactions_details' values Y
searchableText searchable string string N
payload meta-data of the request string N
payloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl Url that the modal is redirected to on a successful completion of the journey string N
failureRedirectUrl Url that the modal is redirected to on a failed journey string N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N

Response

The API response is a transaction ID that is required by the front end SDK.

String Response: Create Accountholder Confirmation Session

<transaction ID>

Get Account Verification Transaction

Fetches a given account verification transaction with its fields decrypted. This call uses your private API key (obtained from your admin dashboard).

By Transaction ID

cURL: Get a Single Account Verification Transaction

curl https://api.paywithcitizen.com/v2/account-verification/<transactionId>
 -X GET \
 -H AuthorizationCitizen: <merchant private api key> \
 -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/account-verification/<transactionId>
 -X GET \
 -H AuthorizationCitizen: <merchant private api key>  \
 -H Content-Type: application/json

JSON Response: Get Account Verification Transaction

{
  "citizenTransactionId":<transaction ID>,
  "merchantId":<your merchant ID>,
  "merchantTradingName":"My Company",
  "customerIdentifier":"john.doe@test.com",
  "financialServiceProvider":"CITIZEN_TEST_BANK",
  "version":"2",
  "journeyType":"NO_EMAIL",
  "transactionType":null,
  "transactionStatus":"ACCEPTED",
  "searchableText":null,
  "payload":null,
  "isPayloadEncrypted":false,
  "creationDate":1647854171186,
  "testParameters":{},
  "accounts":[
    {
      "id":"a4b7f4fdt5g6l341133",
      "accountName":"Citizen Test-Account",
      "accountHolderName":"Citizen Test-Account",
      "number":"57583676",
      "partialAccountNumber":"676",
      "sortCode":"040075",
      "iban":"GB71REVO00997094014496",
      "partialIban":"496",
      "bic":"REVOGB21",
      "balance":"null",
      "type":"account",
      "details":"{\"client_name\":\"Citizen Test-Account\",\"account_number\":\"57583676\",\"sort_code\":\"040075\",\"iban\":\"GB71REVO00997094014496\",\"bic\":\"REVOGB21\"}",
      "bank":"CITIZEN_TEST_BANK"
    }
  ]
}

Headers

Header Description
AuthorizationCitizen Your private merchant API key

Response

Attribute Description
citizenTransactionId identifier for the account verification journey
merchantId your merchant ID
customerIdentifier used to identify the customer whose bank details are fetched
merchantTradingName your merchant trading name
financialServiceProvider customer's bank
version account verification transaction version
journeyType type of joureny used to fetch the customer bank details
transactionStatus status of the journey
creationDate date the journey was initiated
financialServiceProvider the bank that user authorised
accounts customer bank account details

Get Account Verification Bank Unavailable Fallback URL

A fallback URL can be set in your admin dasboard that is used when there is an error redirecting a customer to their bank. It includes the transaction ID as a parameter and may be fetched with this call.

cURL: Get Account Verification Bank Unavailable Fallback URL

curl https://api.paywithcitizen.com/v2/account-verification/bank-unavailable-url/<transactionId> \
  -X GET \
  -H Content-Type: application/json \  
curl https://testapi.paywithcitizen.com/v2/account-verification/bank-unavailable-url/<transactionId> \
  -X GET \
  -H Content-Type: application/json \

Headers

Header Description
Content-Type 'application/json'

Response

The API response is a url that has as query param the transaction ID or null if the fallback url hasn't been set.

Attribute Description
bankUnavailableFallbackURL Fallback URL for the given transaction

String Response: bank unavailable fallback url

Fallback URL, eg: https://www.paywithcitizen.com?transaction-id=9d7de170-ea61-5453-bd05-893876

Verified Pay-In API

Our Verified Pay-In API enables you to receive pay-ins from your customer's verified bank account. Your customer needs at least one verified bank account during this journey. If your customer doesn't have a verified bank account, they will be taken through our account verification journey automatically.

Types of Verified Pay-In Journey

Types without Email with Email with QR code
Without Verified Account account verification transaction & pay-in transaction account verification transaction & pay-in transaction account verification transaction & pay-in transaction
With Verified Account pay-in transaction pay-in transaction pay-in transaction

Create Verified Pay-In Session

Initiate a verified pay-in session on your backend as you will need to provide your private api key (which you can find in your merchant dashboard).

You will then receive a citizenTransactionId. This will be used to start the verified pay-in journey.

cURL: Create Verified Pay-In Session

curl https://api.paywithcitizen.com/v2/payins/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: your merchant private api key \
  -d '{
      customerIdentifier: 12345,
      paymentGiro: FPS,
      amount: 1.00,
      currency: GBP,
      reference: your reference,
      description: your description,
      customerIpAddress: 127.0.0.1,
      customerDeviceOs: user OS,
      searchableText: searchable text,
      payload: {"address":"some address"},
      isPayloadEncrypted: true,
      supportedCountries: ['GB'],
      successRedirectUrl: "https://www.example.com/success",
      failureRedirectUrl: "https://www.example.com/failure",
      disableAddingNewBanks: true
    }'
curl https://testapi.paywithcitizen.com/v2/payins/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: your merchant private api key \
  -d '{
      customerIdentifier: 12345,
      paymentGiro: FPS,
      amount: 1.00,
      currency: GBP,
      reference: your reference,
      description: your description,
      customerIpAddress: 127.0.0.1,
      customerDeviceOs: user OS,
      searchableText: searchable text,
      payload: {"address":"some address"},
      isPayloadEncrypted: true,
      supportedCountries: ['GB'],
      successRedirectUrl: "https://www.example.com/success",
      failureRedirectUrl: "https://www.example.com/failure",
      disableAddingNewBanks: true
    }'

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user string Y
customerEmailAddress The email of the paying customer. Only needed for email journey string N
paymentGiro payment type (SEPA or FPS) string Y
amount decimal value of the payment string with valid 2 decimal number format Y
currency currency string with valid ISO currency code Y
reference payment reference shown to the customer. Needs to be alphanumeric and 15 chars max string Y
description description shown to the customer string N
customerIpAddress IP for the paying customer string with valid IPv4 or IPv6 format Y
customerDeviceOs OS for the paying customer string Y
searchableText searchable string string N
payload meta-data of the payment string N
isPayloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl It redirect the page that the modal is shown in completion of the journey string N
failureRedirectUrl It redirect the page that the modal is shown in case of closing the modal on failed status string N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N
disableAddingNewBanks Prevents user from removing existing bank accounts or adding new ones boolean N

Response

The API response is a citizenTransactionId that is required by the front end SDK.

String Response: Create Verified Pay-In Session

<citizenTransactionId>

Get Customer's Verified Accounts Details

Once you have created your verified pay-in session and retrieved your citizenTransactionId, you can retrieve a list of all the verified accounts a customer has with you, and the session details as well. You will just need to provide your citizenTransactionId and your merchant public api key.

cURL: Get Customer's Verified Accounts Details

curl https://api.paywithcitizen.com/v2/account-verification/verified-payin/accounts/{citizenTransactionId} \
  -X GET \
  -H AuthorizationCitizen: <your merchant public api key> \
  -H Content-Type: application/json 
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payin/accounts/{citizenTransactionId} \
  -X GET \
  -H AuthorizationCitizen: <your merchant public api key> \
  -H Content-Type: application/json 

JSON Response: Get Customer's Verified Accounts Details

{
  "payInSession": {
      "citizenTransactionId": "<unique id for the verified pay-in transaction>",
      "providerTransactionId": "",
      "customerIdentifier": "<the internal id you use on your systems to identity your user>",
      "customerEmailAddress": "<The email of the paying customer>",
      "customerBankAccountId": "<customer selected bank account id>",
      "serviceProvider": "<financial Service Provider, e.g. BARCLAYS>",
      "paymentGiro": "e.g. FPS",
      "amount": "<the amount of the payment>",
      "currency": "<the currency of the payment>",
      "reference": "<a reference for the payment>",
      "description": "<a description for the payment>",
      "transactionStatus": "<status of the transaction>",
      "customerIpAddress": "<IP for the paying customer>",
      "customerDeviceOs": "<OS for the paying customer>",
      "bankAccountNumber": "<bank account for the verified pay-in transaction>",
      "bankCode": "<bank code for the verified pay-in transaction>",
      "searchableText": "<a string that you can use to search for the verified pay-in transaction>",
      "payload": "<a string payload that can take any format>",
      "isPayloadEncrypted": "<boolean value indicated that the payload is encrypted or not>",
      "supportedCountries": "<list of string country>",
      "successRedirectUrl": "<success redirect url>",
      "failureRedirectUrl": "<failure redirect url>"
  },
  "accountTransactions": [
    {
      "citizenTransactionId": "<unique id for the pay-in transaction>",
      "merchantId": "<id of merchant>",
      "merchantTradingName": "<trading name of merchant>",
      "customerIdentifier": "<the internal id you use on your systems to identity your user>",
      "financialServiceProvider": "<financial Service Provider, e.g. BARCLAYS>",
      "version": "<version of api>",
      "journeyType": "<type of journey, e.g. EMAIL/NO_EMAIL>",
      "transactionType": "<type of transaction, e.g. ACCOUNT_VERIFICATION>",
      "transactionStatus": "<status of the transaction>",
      "searchableText": "<a string that you can use to search for the verified pay-in transaction>",
      "payload": "<a string payload that can take any format>",
      "isPayloadEncrypted": "<boolean value indicated that the payload is encrypted or not>",
      "creationDate": "<date of creation>",
      "accounts": [
          {
              "id": "<id of account>",
              "accountName": "<name of account>",
              "accountHolderName": "<account holder name>",
              "number": "<account number>",
              "partialAccountNumber": "<partial account number>",
              "sortCode": "<sort code of account>",
              "iban": "<iban number of account>",
              "partialIban": "<partial Iban number of account>",
              "bic": "<bic number>",
              "balance": "<balance of account>",
              "type": "<>",
              "details": "<description about account>",
              "bank": "<financial Service Provider, e.g. BARCLAYS>"
          }
      ]
    }
  ]
}

Headers

Header Description
AuthorizationCitizen Your merchant public API key
Parameter Description
citizenTransactionId String of the transaction ID to process

Verified Payout API

Our Verified Payout API enables you to send funds to your customer's verified bank account.

Create Verified Payout session

The first step in the process is to create a verified payout session with details of the payout. This request is typically created from your back end. The request uses your merchant private API key (from your admin dashboard) and the body must be signed with the signature set as a request header. The call returns a transaction ID which is used throughout the payout journey.

cURL: Create Verified Payout Session

curl https://api.paywithcitizen.com/v2/payouts/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <private API key> \
  -H Signature: <base 64 encoded signature of the request body> \
  -H Expires-at: <UNIX timestamp> \
  -d '{
      merchantId: <your merchant ID>, \
      customerIdentifier: <your unique identifier for the customer>, \
      currency: GBP, \
      paymentGiro: FPS, \
      amount: 3.50, \
      reference: ref12345, \
      customerIpAddress: 192.168.1.2, \
      customerDeviceOs: iOS 14 \
    }'
curl https://testapi.paywithcitizen.com/v2/payouts/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <private API key> \
  -H Signature: <base 64 encoded signature of the request body> \
  -H Expires-at: <UNIX timestamp> \
  -d '{
      merchantId: <your merchant ID>, \
      customerIdentifier: customer@gmail.com, \
      currency: GBP, \
      paymentGiro: FPS, \
      amount: 3.50, \
      reference: ref1234, \
      customerIpAddress: 192.168.1.2, \
      customerDeviceOs: iOS 14 \
    }'

Headers

Header Description
AuthorizationCitizen Your private API key
Expires-at UNIX timestamp for signature expiration
Signature Base 64 encoded signature
Content-Type 'application/json'

Request

Parameter Description Type Required
merchantId Your merchantId. This can be obtained from the admin dashboard string Y
customerIdentifier Used to identify the customer receiving the payout. This can be an identifier internal to your system string Y
customerEmailAddress Customer email address which is used if using the payout journey string N
amount Decimal value of the payout - string with number with 2 decimal places string Y
currency string with valid ISO currency code string Y
paymentGiro payment type (SEPA or FPS) string Y
reference Reference for the payout (max 15 chars) string Y
customerDeviceOs OS for the customer receiving the payout string Y
customerIpAddress IP address of the customer receiving the payout string with valid IPv4 or IPv6 format Y
searchableText searchable string string N
payload meta-data of the payment string N
payloadEncrypted encrypt payload field (true/false) boolean N
supportedCountries filter the bank list in bank selection page array of string country ISO codes N
successRedirectUrl Url that the user is redirected to from the modal on completion of a successful payout journey string N
failureRedirectUrl Url that the user is redirected to from the modal on completion of a failed payout journey string N
successBankRedirectUrl Url that the user is redirected to from their bank app on a successful approval string N
failureBankRedirectUrl Url that the user is redirected to from their bank app on a cancelled/failed request string N
queue determine if the payout will added to queue and waiting for manual confirmation or submitted right away boolean N
heldReason the reason that a payout added to a queue string N
disableAddingNewBanks Prevents user from removing existing bank accounts or adding new ones boolean N

Response

The API response is a transaction ID that is required by the front end SDK.

String Response: Create Verified Payout Session

<transaction ID>

Get Valid Bank Accounts For Payout

After the user has authorised the account verification with their bank, their bank account details have now been verified and can be accessed with the following call. This call uses your public API key. After a bank account has been verified it can be used for future payouts without the need to do the account verification journey again.

cURL: Get Valid Bank Accounts For Payout

curl https://api.paywithcitizen.com/v2/account-verification/verified-payout/accounts/<transaction ID> \
  -H AuthorizationCitizen: <your public API key> \
  -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payout/accounts/<transaction ID> \
  -H AuthorizationCitizen: <your public API key> \
  -H Content-Type: application/json

Headers

Header Description
AuthorizationCitizen Your public API key
Parameter Description Type
transactionID Transaction ID to process String

Response

The response contains details of verified bank accounts that the payout may be sent to

String Response: Get Valid Bank Accounts For Payout

{
    "accountTransactions": [
        {
            "citizenTransactionId":<transaction ID>,
            "merchantId":<your merchant ID>,
            "merchantTradingName":"My merchant",
            "customerIdentifier":"test1@citizen.is",
            "financialServiceProvider":"CITIZEN_TEST_BANK",
            "version":"2",
            "journeyType": "VERIFIED_PAYOUT_NO_EMAIL",
            "transactionStatus":"ACCEPTED",
            "creationDate":1646997683853,
            "testParameters":{},
            "accounts": [ 
                {
                    "id":"A3GbeDIfc6f99ecDJKLC", 
                    "accountName":"Citizen Test-Account",
                    "accountHolderName":"Citizen Test-Account",
                    "partialAccountNumber":"676",
                    "partialIban":"496",
                    "type":"account",
                    "bank":"CITIZEN_TEST_BANK"
                }
            ]
        }
    ]
}

Get the payout transaction

Details of the payout transaction can be fetched at any stage after its creation. This call uses your private API key and the payout transaction ID as the past parameter of its path.

cURL: Get Verified Payout Transaction

curl https://api.paywithcitizen.com/v2/payouts/transaction/<transaction ID> \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <private API key>
curl https://testapi.paywithcitizen.com/v2/payouts/transaction/<transaction ID> \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <private API key>

Response: Get Payout Transaction

{
    "citizenTransactionId":<transaction ID>,
    "merchantId":<your merchant ID>,
    "merchantTradingName":"My merchant",
    "customerIdentifier":<your unique identifier for the customer>,
    "transactionStatus":"COMPLETE",
    "creationDate":1647010433548,
    "testParameters":{},
    "citizenCounterPartyId":"622b62816a47c2389498b875",
    "providerPaymentId":"296348",
    "currency":"GBP",
    "paymentGiro":"FPS",
    "merchantBankCode":"608384",
    "merchantAccountNumber":"40074844",
    "merchantBank":"CONTIS",
    "counterPartyBank":"CITIZEN_TEST_BANK",
    "counterPartyBankCode":"040075",
    "counterPartyAccountNumber":"57583676",
    "counterPartyAccountName":"CitizenTest Account",
    "amount":10.00,
    "reference":"Test payment"
}
Header Description
AuthorizationCitizen Your private API key
Parameter Description Type
citizenTransactionId Transaction ID to process String

Response

Payout transaction details.

Confirm a queued Verified Payout

Once you have created your verified payout session and retrieved your citizenTransactionId you can confirm a payout that is added to queue.

cURL: Confirm payout

curl https://api.paywithcitizen.com/v2/payouts/transaction/confirm/<transaction ID> \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json \
  -H Signature: Base 64 encoded signature \
  -H Expires-at: UNIX timestamp for signature expiration
curl https://testapi.paywithcitizen.com/v2/payouts/transaction/confirm/<transaction ID> \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json \
  -H Signature: Base 64 encoded signature \
  -H Expires-at: UNIX timestamp for signature expiration

JSON Response: Payout transaction details

{
    "citizenTransactionId":<transaction ID>,
    "merchantId":<your merchant ID>,
    "merchantTradingName":"My merchant",
    "customerIdentifier":<your unique identifier for the customer>,
    "transactionStatus":"ACCEPTED",
    "creationDate":1647010433548,
    "citizenCounterPartyId":"622b62816a47c2389498b875",
    "providerPaymentId":"296348",
    "currency":"GBP",
    "paymentGiro":"FPS",
    "merchantBankCode":"608384",
    "merchantAccountNumber":"40074844",
    "merchantBank":"CONTIS",
    "counterPartyBank":"CITIZEN_TEST_BANK",
    "counterPartyBankCode":"040075",
    "counterPartyAccountNumber":"57583676",
    "counterPartyAccountName":"CitizenTest Account",
    "amount":10.00,
    "reference":"Test payment"
}

Headers

Header Description
AuthorizationCitizen Your merchant private API key
Signature The signature that you sign the request. You can find how to generate the signature here
Expires-at The expiration time of the signature on unix timestamp format.
Parameter Description
citizenTransactionId String of the transaction ID to process

Response

Payout transaction details.

Reject a queued Verified Payout

Once you have created your verified payout session and retrieved your citizenTransactionId you can reject a payout that is added to queue.

cURL: Reject payout

curl https://api.paywithcitizen.com/v2/payouts/transaction/reject \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json \
  -H Signature: Base 64 encoded signature \
  -H Expires-at: UNIX timestamp for signature expiration \
  -d ' {
        "citizenTransactionId": "<transactionID>",
        "rejectionReason": "the reason why this payout has been rejected"
}'
curl https://testapi.paywithcitizen.com/v2/payouts/transaction/reject \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json \
  -H Signature: Base 64 encoded signature \
  -H Expires-at: UNIX timestamp for signature expiration \
  -d ' {
        "citizenTransactionId": "<transactionID>",
        "rejectionReason": "the reason why this payout has been rejected"
}'

JSON Response: Payout transaction details

{
    "citizenTransactionId":<transaction ID>,
    "merchantId":<your merchant ID>,
    "merchantTradingName":"My merchant",
    "customerIdentifier":<your unique identifier for the customer>,
    "transactionStatus":"CANCELLED_BY_MERCHANT",
    "creationDate":1647010433548,
    "citizenCounterPartyId":"622b62816a47c2389498b875",
    "providerPaymentId":"296348",
    "currency":"GBP",
    "paymentGiro":"FPS",
    "merchantBankCode":"608384",
    "merchantAccountNumber":"40074844",
    "merchantBank":"CONTIS",
    "counterPartyBank":"CITIZEN_TEST_BANK",
    "counterPartyBankCode":"040075",
    "counterPartyAccountNumber":"57583676",
    "counterPartyAccountName":"CitizenTest Account",
    "amount":10.00,
    "reference":"Test payment"
}

Headers

Header Description
AuthorizationCitizen Your merchant private API key
Signature The signature that you sign the request. You can find how to generate the signature here
Expires-at The expiration time of the signature on unix timestamp format.
Parameter Description Required
citizenTransactionId String of the transaction ID to process Y
rejectionReason String of the reason why the payout has been rejected N

Response

Payout transaction details.

Account Management API

Our Account Management API enables you to create and update your Citizen account, or accounts you manage from your own apps and services.

Register Merchant

When registering your merchant, you must provide a user email and password. This user will be your merchant admin. The trading name you provided will be used in emails or any other Citizen forms.

cURL: Register Merchant

curl https://api.paywithcitizen.com/v2/merchant/register \
  -X POST \
  -H Content-Type: application/json \
  -d {
       email: <Email for the user you want to set as a merchant admin>, \
       password: <Password for the user you want to set a merchant admin>, \
       tradingName: <Name of merchant used in emails and other Citizen forms>
      }
curl https://testapi.paywithcitizen.com/v2/merchant/register \
  -X POST \
  -H Content-Type: application/json \
  -d {
       email: <Email for the merchant admin>, \
       password: <Password for the merchant admin>, \
       tradingName: <Name of merchant used in emails and other Citizen forms>
     }

Headers

Header Description
Content-Type 'application/json'

Request

Parameter Description Required
email the email address of your administrator (eg admin@yourcompany.com) Y
password a strong passphrase for your administrator Y
tradingName your merchant's trading name Y

Response

Registered merchant details will be returned, you can save these details for future usage.

JSON Response: Registered Merchant Details

{
  "id": "<Id of merchant. This merchantId will be used in other requests.>",
  "email": "<The email address of your administrator>",
  "tradingName": "<Trading name of merchant>",
  "apiKeys": "<Your merchant private API key>",
  "publicApiKeys": "<Your merchant public API key>",
  "merchantUserIds": "<Admin users of the merchant>",
  "rootAdminUserId": "<First admin of the merchant>"
}

Get Merchant Details

You will just need to provide your merchantId your merchant private api key.

cURL: Get merchant details

curl https://api.paywithcitizen.com/v2/merchant/{merchantId} \
  -X GET \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/merchant/{merchantId} \
  -X GET \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Response

The merchant details and decrypted bank account details will be returned.

JSON Response: Registered Merchant And Acquiring Account Details

{
  "id": "<Id of merchant>",
  "email": "<The email address of your administrator>",
  "tradingName": "<Trading name of merchant>",
  "apiKeys": "<Your merchant private API key>",
  "publicApiKeys": "<Your merchant public API key>",
  "merchantUserIds": "<Admin users of the merchant>",
  "rootAdminUserId": "<First admin of the merchant>",
  "payInBankDetails": {
      "accountName": "<The account name you provided>",
      "accountNumber": "<Decrypted acquiring account number>",
      "sortCode": "<Decrypted acquiring account sort code>",
      "iban": "<Decrypted acquiring account iban>",
      "bic": "<Decrypted acquiring account bic>",
      "providerBankAccountId": "<The bank account id that can be identified by provider>",
      "providerCustomerId": "<The customer id that can be identified by provider>"
  },
  "payOutBankDetails": {
    "accountName": "<The account name you provided>",
    "accountNumber": "<Decrypted acquiring account number>",
    "sortCode": "<Decrypted acquiring account sort code>",
    "iban": "<Decrypted acquiring account iban>",
    "bic": "<Decrypted acquiring account bic>",
    "providerBankAccountId": "<The bank account id that can be identified by provider>",
    "providerCustomerId": "<The customer id that can be identified by provider>"
  }
}

Generate New Merchant API Key

You only need to provide your merchant private api key to make this request. The old api key will still valid. You can get merchant details to check all the api keys you have.

cURL: Generate New Merchant API Key

curl https://api.paywithcitizen.com/v2/merchant/generate-new-api-key \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json
curl https://testapi.paywithcitizen.com/v2/merchant/generate-new-api-key \
  -X POST \
  -H AuthorizationCitizen: <your merchant private api key> \
  -H Content-Type: application/json

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Response

A new merchant private api key will be returned.

String Response: Returned A New Merchant API Key

<new merchant private api key>

Disable Merchant API Key

You need to provide your admin api key and the api key you want to disable to make this request. You can get merchant details to check all the api keys you have.

cURL: Disable Merchant API Key

curl https://api.paywithcitizen.com/v2/merchant/disable-api-key \
  -X DELETE \
  -H AuthorizationCitizen: <your admin api key> \
  -H Content-Type: application/json \
  -d { \
      '<the api key you want to disable>' \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/disable-api-key \
  -X DELETE \
  -H AuthorizationCitizen: <your admin api key> \
  -H Content-Type: application/json \
  -d { \
      '<the api key you want to disable>' \
     }

Headers

Header Description
AuthorizationCitizen Your amdin api key
Content-Type 'application/json'

Request

Parameter Description Required
apiKeyToDisable the api key you want to disable Y

Response

Return an empty body if successfully disable the api key.

Update Pay-In Success Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the success redirect URL. However, you can use this request to change the URL your customers are sent to when they complete a pay-in. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the payInSuccessRedirect is set to the url you provided.

cURL: Updating pay-in success redirect url

curl https://api.paywithcitizen.com/v2/merchant/pay-in-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/pay-in-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after a success pay-in String Y

Update Pay-In Failure Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the failure redirect URL. However, you can use this request to change the URL your customers are sent to when a pay-in failed. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the payInFailureRedirect is set to the url you provided.

cURL: Updating pay-in failure redirect url

curl https://api.paywithcitizen.com/v2/merchant/pay-in-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/pay-in-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after a pay-in failed String Y

Update Account Verification Success Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the success redirect URL. However, you can use this request to change the URL your customers are sent to when they complete an account verification. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the accountVerificationSuccessRedirect is set to the url you provided.

cURL: Updating account verification success redirect url

curl https://api.paywithcitizen.com/v2/merchant/account-verification-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/account-verification-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after a success account verification String Y

Update Account Verification Failure Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the failure redirect URL. However, you can use this request to change the URL your customers are sent to when the account verification failed. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the accountVerificationFailureRedirect is set to the url you provided.

cURL: Updating account verification failure redirect url

curl https://api.paywithcitizen.com/v2/merchant/account-verification-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/account-verification-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after account verification failed String Y

Update Payout Success Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the success redirect URL. However, you can use this request to change the URL your customers are sent to when they complete a payout. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the payoutRedirect is set to the url you provided.

cURL: Updating payout success redirect url

curl https://api.paywithcitizen.com/v2/merchant/payout-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/payout-success-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after a success payout String Y

Update Payout Failure Redirect URL

We provide a default URL(https://www.paywithcitizen.com/deposit-processing) if you don't set up the failure redirect URL. However, you can use this request to change the URL your customers are sent to when a payout failed. You only need to provide your merchant private api key and a valid redirect URL. You can use get merchant details endpoint to see the payoutFailureRedirect is set to the url you provided.

cURL: Updating payout failure redirect url

curl https://api.paywithcitizen.com/v2/merchant/payout-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/payout-failure-redirect \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "url": "https://www.paywithcitizen.com/deposit-processing" \
     }

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the webpage your customers are sent to after a payout failed String Y

Add Merchant Webhook

You need to provide which event types to be posted to the url(webhook), and your merchant private api key.

cURL: Add webhooks for the merchant

curl https://api.paywithcitizen.com/v2/merchant/webhook \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "eventTypes": ["PAYIN_CREATED"], \
        "url": "www.paywithcitizen.com", \
        "version": "2" \
     }
curl https://testapi.paywithcitizen.com/v2/merchant/webhook \
  -X PATCH \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d { \
        "eventTypes": ["PAYIN_CREATED"], \
        "url": "www.paywithcitizen.com", \
        "version": "2" \
     }

Event Type

Event Type Name
PAYIN_CREATED
PAYIN_REDIRECT
PAYIN_DECISION
PAYIN_ERROR
PAYOUT_CREATED
PAYOUT_ACCEPTED
PAYOUT_QUEUED
PAYOUT_ERROR
PAYOUT_REJECTED
ACCOUNT_VERIFICATION_CREATED
ACCOUNT_VERIFICATION_DECISION
ACCOUNT_VERIFICATION_REDIRECT
ACCOUNT_VERIFICATION_ERROR

Remove Merchant Webhook

You need to provide the webhook url that you would like to be removed.

cURL: Remove webhooks for the merchant

curl https://api.paywithcitizen.com/v2/merchant/webhook \
  -X DELETE \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d '<webhookUrl>'
curl https://testapi.paywithcitizen.com/v2/merchant/webhook \
  -X DELETE \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d '<webhookUrl>'

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
url the url that will be removed from the webhooks String Y

Corporate Account API

Our Corporate Account API enables you to get your corporate account information and transactions.

Get corporate accounts

You can get your corporate accounts linked to your merchant using your merchant private api key.

cURL: Get Corporate accounts

curl https://api.paywithcitizen.com/v2/corporate-account/merchant/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \

curl https://testapi.paywithcitizen.com/v2/corporate-account/merchant/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Response

The API response is your merchant corporate account.

String Response: your merchant corporate account

{
    "accountId": "<the id of the account>",
    "merchantDescription": "<friendly name of the account>",
    "customerNumber": "<the id of the owner>",
    "accountNumber": "<the account number or iban depending the giro>",
    "bankCode": "<sort code or bic depending the giro>",
    "giro": "<the giro of the account>",
    "currency": "<the currency of the account>",
    "balance": "<the available balance of the account>",
    "providerBankAccountId": "<the provide identifier of the account>",
    "accountHolderName": "<the account holder name of the account>"
}

Get corporate account transactions

You can get your corporate account transaction using your merchant private api key, and the provider bank account Id.

cURL: Get Corporate Account Transactions

curl https://api.paywithcitizen.com/v2/corporate-account/transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \

curl https://testapi.paywithcitizen.com/v2/corporate-account/transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request Path Variables

Parameter Description
providerBankAccountId The identifier of the account on provider's system

Request Query Parameters

Parameter Description Type Required
page The page number for the pagination Integer Y
size The size of the page (max 200) Integer Y
dateFrom The date from when the transaction were created (e.g. 2022-01-25) default time of 00:00) String N
dateTo The date up to when the transaction were created (e.g. 2022-02-30) default time of 00:00 String N

Response

The API response is a list of corporate account transactions.

Parameter Description Type
description(Deprecated) This the short reference of the payment String
reference This the reference of the payment String
transactionType The transaction type. The list of all the transaction types can be found here String
transactionId The ID representing the transaction within the bank account(not necessary the same as CitizenTransactionId) String
clientReferenceNumber(Deprecated) This is the transaction id of the payment String
transactionAmount The transaction amount of the payment String
transactionCurrency The currency String with valid ISO currency code
counterPartyFirstName The first name of the requester String
counterPartyLastName The last name of the requester String
transactionDate The date when the transaction were created (e.g. 2021-02-04T13:28:07.397Z) String
debit Shows if the transaction is debit or not. Boolean

List of the corporate account transactions

{
  "content": [
    {
    "reference": "<the reference of the transaction>",
    "transactionType": "<the type of the transaction>",
    "transactionId": "<the id of the transaction>",
    "transactionAmount": "<the amount of the transaction>",
    "transactionCurrency": "<the currency of the transaction>",
    "counterPartyFirstName": "<the first name of the person that created the transaction>",
    "counterPartyLastName": "<the last name of the person that created the transaction>",
    "transactionDate": "<the creation date of the transaction>",
    "debit": "<shows if the transaction is debit or not>" 
  }
  ],
  "totalPages": "<total pages number>"
}

Available transaction types

Value Description
PAYIN Money coming in from an external account
PAYOUT Money coming out from an external account
TRANSFER Money transferred internally between Citizen accounts

Download corporate account transactions by CurrencyCode

Use your merchant private api key and specify the currency of your corporate account, then you can download all the matched transactions of that corporate account. Currently, we only support GBP and EUR currency.

cURL: Download corporate account transactions by CurrencyCode

curl https://api.paywithcitizen.com/v2/corporate-account/transactions/currency/{currencyCode}/generate-csv \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d '{
      dateFrom: 2023-01-01,
      dateTo: 2023-02-01,
      amountFrom: 1.00,
      amountTo: 10.00
    }'
curl https://testapi.paywithcitizen.com/v2/corporate-account/transactions/currency/{currencyCode}/generate-csv \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your merchant private api key> \
  -d '{
      dateFrom: 2023-01-01,
      dateTo: 2023-02-01,
      amountFrom: 1.00,
      amountTo: 10.00
    }'

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Content-Type 'application/json'

Request

Parameter Description Type Required
dateFrom The date from when the transaction were created (e.g. 2022-01-25) default time of 00:00) String N
dateTo The date up to when the transaction were created (e.g. 2022-02-30) default time of 00:00 String N
amountFrom Filter out the transactions less than this amount string with valid 2 decimal number format N
amountTo Filter out the transactions more than this amount string with valid 2 decimal number format N

Response

The API response includes the csv file name and base64 string. Please refer to the code snippet about how to convert the base64 string to csv either in your backend or frontend.

Json Response: the csv filename and Base64 string

{
    "filename": "<fileName>.csv",
    "data": "<Base64 String>"
}

Kotlin: Convert base64 to csv in your backend

val base64String = "TWVyY2hhbnQgSWQsUmVmZXJlbmNlLFRyYW5zYWN0aW9uIFR5cGUsUHJvdmlkZXIgVHJhbnNhY3Rpb24gSWQsVHJhbnNhY3Rpb24gQW1vdW50LFRyYW5zYWN0aW9uIEN1cnJlbmN5LE5hbWUsVHJhbnNhY3Rpb24gRGF0ZSxBY2NvdW50IEJhbGFuY2UsQ2l0aXplbiBUcmFuc2FjdGlvbiBJZAo2MmRhOGE4OThlYTk5YTUwYzJjMDMwMWMsMCwwLDAsMCwwLDAsMCwwLDA="
val csvFile = File("output.csv")

val decodedBytes = Base64.getDecoder().decode(base64String)
val csvString = String(decodedBytes, Charsets.UTF_8)

BufferedWriter(FileWriter(csvFile)).use { 
    writer -> writer.write(csvString)
}

Java: Convert base64 to csv in your backend

String base64String = "TWVyY2hhbnQgSWQsUmVmZXJlbmNlLFRyYW5zYWN0aW9uIFR5cGUsUHJvdmlkZXIgVHJhbnNhY3Rpb24gSWQsVHJhbnNhY3Rpb24gQW1vdW50LFRyYW5zYWN0aW9uIEN1cnJlbmN5LE5hbWUsVHJhbnNhY3Rpb24gRGF0ZSxBY2NvdW50IEJhbGFuY2UsQ2l0aXplbiBUcmFuc2FjdGlvbiBJZAo2MmRhOGE4OThlYTk5YTUwYzJjMDMwMWMsMCwwLDAsMCwwLDAsMCwwLDA=";
File csvFile = new File("output.csv");

byte[] decodedBytes = Base64.getDecoder().decode(base64String);
String csvString = new String(decodedBytes, "UTF-8");

try (BufferedWriter writer = new BufferedWriter(new FileWriter(csvFile))) {
    writer.write(csvString);
} catch (IOException e) {
    e.printStackTrace();
}

Javascript: Convert base64 to csv to a downloadable link in the browser

export const download = (filename, data) => {
  let element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;base64,' + data);
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

Redirects

Throughout our multiple journeys, there are a number of points where we provide you the ability to redirect your users to a customisable url. In general there are 3 types of redirects:

  1. Redirecting from the page that the SDK modal is shown on. (This is the case for either the QR code or Email journey. This is not available for direct mobile journeys)

  2. A redirect from the user’s banking app once they have approved/declined a request.

  3. A redirect for our “bank unavailable“ button which is shown on the bank selection page. This is used when the customer's bank is not in the bank selector list, either because it is not using Open Banking protocol or we are in progress of integrating it.

Configure SDK modal redirects

You can configure the success and failure redirects for the page that the modal is shown when you send the request to create the session for each journey (payins/account-verification/verified-payins/payouts)

The success redirect is used when a journey is successfully authorised and accepted.

The failure redirect is used when the journey fails. This can be due to an internal server error, error submitting to the bank, cancelled by the user, etc.

PayIn SDK redirect

You can check the payin session creation section to see the request and the attributes for this.

Verified PayIn SDK redirect

You can check the verified payin session creation section to see the request and the attributes for this.

Account Verification SDK redirect

You can check the account verification session creation section to see the request and the attributes for this.

Payout SDK redirect

You can check the payout session creation section to see the request and the attributes for this.

Configure bank redirects

You can configure where the user will be redirected to when they authorise or reject a transaction from their banking app

The user will redirect to the (use the exact name in the dashboard) url if the transaction is successfully authorised within the bank app.

If for any reason the request fails to go through, the failure redirect will be used.

Failure reasons are:

  1. Internal server error

  2. External errors

  3. User cancelled the journey

Dynamic Bank Redirects

These are dynamic redirects where your user will be redirected to from their banking app. These will override your default bank redirects. If these are not set, we will use the default redirects instead.

You set these dynamic bank redirects on session creation:

PayIn Bank Redirect

You can check the payin session creation section to see the request and the attributes for this.

Verified PayIn Bank Redirect

You can check the verified payin session creation section to see the request and the attributes for this.

Account Verification Bank Redirect

You can check the account verification session creation section to see the request and the attributes for this.

Payout Bank Redirect

You can check the payout session creation section to see the request and the attributes for this.

Configure Default Payin Bank Redirects

To configure your pay-in processing and failure redirects, you need to go to the Citizen Admin Dashboard, under Company Settings in the tab PayIn settings. The field "Payment Authorization Processing Url" and "Payment Failure Authorization url" are the processing and failure redirect urls respectively.

There fields applied to the verified payin journey on the payment part of the journey.








Configure Default Account Verification Bank Redirects

To configure Account Verification processing and failure redirect you need to go to Citizen Admin Dashboard, under Company Settings in the tab Account Verification Settings. The field "Account Verification Authorization Processing Url" and "Account Verification Failure Authorization url" are the processing and failure redirect urls respectively.

The field "Account Verification Failure Authorization url" will apply to verified payIn and payout journey on the verification part of the journey.








Configure Default Payout Bank Redirects

To configure Payout processing and failure redirect you need to go to Citizen Admin Dashboard, under Company Settings in the tab Payout Settings. The field "Payout Authorization Processing Url" and "Payout Failure Authorization url" are the processing and failure redirect urls respectively.

There fields will be applied in payout journey on the submission of the payout.

Configure bank unavailable redirects

You can set a custom redirect to take the user to if their bank is not available. The default value is https://www.paywithcitizen.com/bank-not-available.












Configure Payin Bank Unavailable Redirect

To configure the pay-in bank unavailable url redirect you need to go to Citizen Admin Dashboard, under Company Settings in the tab PayIn settings. The field "Bank unavailable fallback Url" is the bank unavailable redirect url.







Configure Account Verification Bank Unavailable Redirect

To configure Account Verification bank unavailable url redirect you need to go to Citizen Admin Dashboard, under Company Settings in the tab Account Verification settings. The field "Account Verification Bank Unavailable Fallback Url" is the bank unavailable redirect url.

The field "Account Verification Bank Unavailable Fallback Url" will apply to verified payIn on the verification part of the journey.

Configure Payout Bank Unavailable Redirect

To configure Payout bank unavailable url redirect you need to go to Citizen Admin Dashboard, under Company Settings in the tab Payout Settings. The field "Payout Bank Unavailable Fallback Url" is the bank unavailable redirect url.

The field "Payout Bank Unavailable Fallback Url" will apply to payout on the verification part of the journey.

Withdrawals

Withdrawals allow funds to be transferred to external bank accounts for business processes such as acquiring funds, transfers to another merchant or similar. They differ from verified payouts in that the payout bank account does not need to be verified by its owner - its details are instead sent by a signed REST call from the merchant's back end.

Creating Counter Party

The first step to make withdrawals is to create a counter party. This request is typically created from your back end. The request uses your merchant private API key (from your admin dashboard) and the body must be signed with the signature set as a request header.

cURL: Create Counter Party

curl https://api.paywithcitizen.com/v2/corporate-account/admin-counter-party \
    -X POST
    -H "AuthorizationCitizen: <your merchant private api key>" \
    -H "Signature: <request signature>" \
    -H "Expires-at: <signture expiry unix timestamp>" \
    -d '{
            "customerIdentifier": "1846593725421829",
            "bankCountry": "GB",
            "accountGiro": "FPS",
            "accountCurrency": "GBP",
            "accountName": "Internal Account",
            "accountNumber": "12345678",
            "bankCode": "010203",
            "counterPartyBank": "OPENPAYD",
            "customerType": "CORPORATE"
        }'
curl https://testapi.paywithcitizen.com/v2/corporate-account/admin-counter-party \
    -X POST
    -H "AuthorizationCitizen: <your merchant private api key>" \
    -H "Signature: <request signature>" \
    -H "Expires-at: <signture expiry unix timestamp>" \
    -d '{
            "customerIdentifier": "1846593725421829",
            "bankCountry": "GB",
            "accountGiro": "FPS",
            "accountCurrency": "GBP",
            "accountName": "Internal Account",
            "accountNumber": "12345678",
            "bankCode": "010203",
            "counterPartyBank": "OPENPAYD",
            "customerType": "CORPORATE"
        }'

Headers

Header Description
AuthorizationCitizen Your merchant private api key
Signature Base 64 encoded signature
Expires-at UNIX timestamp for signature expiration
Content-Type 'application/json'

Request

Parameter Description Type Required
customerIdentifier The internal id you use on your systems to identity your user string Y
bankCountry The jurisdiction of the counter party account. it should follow the country ISO codes. For example supported countries are [GB], [IE], [NL] String Y
accountGiro Payment rail for the counter party bank account. Depending of the currency of the transaction it can be either FPS (UK faster payments) or SEPA (EU SEPA Inst) String Y
accountCurrency Counter party account currency. The supported currencies are "GBP" and "EUR". String Y
accountName Account name of the counter party String Y
accountNumber For transaction within the UK the sort code or for EUR the iban for the counter party String Y
bankCode The counter party sort code/bic String Y
counterPartyBank Bank of the counter party. If this account was created via a third party such as OPENPAYD, then this should be set to "OPENPAYD". String Y
customerType Type of the counter party. If this account is used for withdrawal in admin dashboard, then this should be set to "CORPORATE". Otherwise, using "RETAIL" for regular payouts String Y

Response

JSON Response: Create Counter Party

{
  "counterPartyId": "62a5ff6a5410953586eea940",
  "merchantId": "62a325fed1b29b74d10791b9",
  "payoutProviderCustomerId": "7bfe1fb4-70ff-480f-83a1-ef8df3ad4d01",
  "payoutProviderAccountId": "clJVZ003Q2RzWnc5cC90ZFdZQ2Z1UT09",
  "customerType": "CORPORATE",
  "customerIdentifier": "1846593725421829",
  "bankCountry": "GB",
  "accountCurrency": "GBP",
  "accountGiro": "FPS",
  "accountNumber": "73200505",
  "bankCode": "231470",
  "accountName": "test Company",
  "merchantBank": "OPENPAYD",
  "counterPartyBank": "OPENPAYD"
}

Authentication

Using the API

Citizen generates two different types of authentication.

This is used to authenticate with the Citizen API.

This is used by the front end SDK to initiate any instructions created by your back end.

API key

This API key is generated on account creation.

You can find your merchant API key in the API Keys section of your admin dashboard.

If you are using our API directly, these keys are returned in the register merchant response.

Public API key

The public API key is generated on account creation.

You can find your merchant public API key in the API Keys section of your admin dashboard.

If you are using our API directly, these keys are returned in the register merchant response.

Use this key for your front end implementation.

Signatures

Signatures provide authentication and also integrity protection. Part of our supported functionality requires the merchant to sign certain requests. In that way it is possible to verify that the request has originated from the right merchant but also provide data integrity.

Create a Public/Private Key Pair

Bash: Generate private key

openssl genpkey -algorithm RSA -outform PEM -out private_key.pem -pkeyopt rsa_keygen_bits:2048

A prerequisite for signing any request is to generate an RSA 2048 keypair using OpenSSL. By executing the first block, a 2048 bit RSA key named private_key.pem is generated, which will be the private key.

Bash: Generate public key

openssl rsa -in private_key.pem -pubout > public_key.pub

The second step is generating a public key based on the private key. This can be easily done by executing the second block of code, which will generate a public key named public_key.pub

Register your Public Key

For our system to be able to verify your payout requests, the public component of the openssl RSA key is used to sign the requests. This key should be registered on our system through the admin dashboard.

Request Authorisation Token

An authorisation token will be provided by Citizen for the merchant to be able to start the payout journey. When the token is provided to you, you can submit it on the admin dashboard under the payout section.

Embedded View|512x397,10%

Register Google authenticator

Once the token has been entered, you will need to set up Google authenticator to make sure the key cannot be easily changed.

Embedded View|512x397,10%

Register Public component

Once Google Authenticator has been setup, you can register the public key from your RSA key pair that you will use to sign requests.

Embedded View|512x397,10%

No Authorisation token

If there isn't any authorisation token registered for your entity, you need to ask for one from Citizen and try again after receiving it.

Embedded View|100x100

Signing requests

Java: Signing a request


public class CounterPartyDetails implements Serializable{
    private static final long serialVersionUID = 212345678976543211L;
    private String customerIdentifier; // The internal id you use on your systems to identity your use
    private String bankCountry; // GB or EUR countires
    private String accountGiro; //Either FPS or SEPA
    private String accountCurrency; // GPB or EUR
    private String accountName; // Account name of the counter party
    private String accountNumber; // Bank account number of the counter party
    private String bankCode; //Either SORT CODE or BIC
    private String counterPartyBank; // OPENPAYD or third party
    private String customerType; // Either COPRORATE or RETAIL
}


@RestController
@RequestMapping(value = "add-counterparty-endpoint")
public class AddCounterParty {

    //Your backend will then make a request to the Citizen service to add a counterparty.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-add-counterparty-endpoint")
    public Callable<ResponseEntity<TextNode>> addCounterParty(TransactionDetails details) {

      CounterPartyDetails counterPartyDetails = new CounterPartyDetails();
      counterPartyDetails.setCustomerIdentifier("1846593725421829");//
      counterPartyDetails.setbankCountry("GB");
      counterPartyDetails.setPaymentGiro(PaymentGiro.FPS);
      counterPartyDetails.setCurrency("GBP");
      counterPartyDetails.setAccountName("Internal Account");
      counterPartyDetails.setAccountNumber("12345678");
      counterPartyDetails.setbankCode("010203");
      counterPartyDetails.setCounterPartyBank("OPENPAYD");
      counterPartyDetails.setcustomerType("CORPORATE");


      RestTemplate restTemplate = new RestTemplate();

      String citizenAddCounterpartyUrl = "https://api.paywithcitizen.com/v2/corporate-account/admin-counter-party";

      HttpHeaders httpHeaders = generateHeadersForSignedRequest(HttpMethod.POST, 
                                                        citizenAddCounterpartyUrl, 
                                                        counterPartyDetails, 
                                                        DB.getPrivateKey);

      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> payoutInitResponse = restTemplate
      .exchange(citizenAddCounterpartyUrl, HttpMethod.POST,
      new HttpEntity<>(counterPartyDetails, httpHeaders), TextNode.class);

      return ResponseEntity.ok(); //Return this to your front end
      }
}

private HttpHeaders generateHeadersForSignedRequest (
     HttpMethod httpMethod, String url, Object requestBody, PrivateKey privateKey)
     throws JsonProcessingException, SignatureException {

     String requestExpiryTime = String.valueOf(Instant.now().getEpochSecond() + 300);

     String textToSign = requestExpiryTime + "|" + httpMethod.name().toUpperCase() + "|" + url + "|";

    if (requestBody != null) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        ObjectWriter objectWriter = objectMapper.writer();
        String requestJson = objectWriter.writeValueAsString(requestBody);
        textToSign += requestJson;
    }

    String signature = generateSignature(textToSign, privateKey);

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.set("Signature", signature);
    httpHeaders.set("Expires-at", requestExpiryTime);

    return httpHeaders;
}


private String generateSignature(String plainText, PrivateKey privateKey)
    throws SignatureException {

    try {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(plainText.getBytes("UTF8"));
        byte[] valueSigned = signature.sign();

        return Base64.getEncoder().encodeToString(valueSigned);
    } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
        throw new SignatureException(e);
    }
}



public class CounterPartyDetails implements Serializable{
    private static final long serialVersionUID = 212345678976543211L;
    private String customerIdentifier; // The internal id you use on your systems to identity your use
    private String bankCountry; // GB or EUR countires
    private String accountGiro; //Either FPS or SEPA
    private String accountCurrency; // GPB or EUR
    private String accountName; // Account name of the counter party
    private String accountNumber; // Bank account number of the counter party
    private String bankCode; //Either SORT CODE or BIC
    private String counterPartyBank; // OPENPAYD or third party
    private String customerType; // Either COPRORATE or RETAIL
}


@RestController
@RequestMapping(value = "add-counterparty-endpoint")
public class AddCounterParty {

    //Your backend will then make a request to the Citizen service to add a counterparty.
    @RequestMapping(method = RequestMethod.POST, path = "/your-example-add-counterparty-endpoint")
    public Callable<ResponseEntity<TextNode>> addCounterParty(TransactionDetails details) {

      CounterPartyDetails counterPartyDetails = new CounterPartyDetails();
      counterPartyDetails.setCustomerIdentifier("1846593725421829");//only needed for email journey
      counterPartyDetails.setbankCountry("GB");
      counterPartyDetails.setPaymentGiro(PaymentGiro.FPS);
      counterPartyDetails.setCurrency("GBP");
      counterPartyDetails.setAccountName("Internal Account");
      counterPartyDetails.setAccountNumber("12345678");
      counterPartyDetails.setbankCode("010203");
      counterPartyDetails.setCounterPartyBank("OPENPAYD");
      counterPartyDetails.setcustomerType("CORPORATE");


      RestTemplate restTemplate = new RestTemplate();

      String citizenAddCounterpartyUrl = "https://testapi.paywithcitizen.com/v2/corporate-account/admin-counter-party";

      HttpHeaders httpHeaders = generateHeadersForSignedRequest(HttpMethod.POST, 
                                                        citizenAddCounterpartyUrl, 
                                                        counterPartyDetails, 
                                                        DB.getPrivateKey);

      httpHeaders.set("AuthorizationCitizen", [YOUR_ENTITY_API_KEY]]);

      ResponseEntity<TextNode> payoutInitResponse = restTemplate
      .exchange(citizenAddCounterpartyUrl, HttpMethod.POST,
      new HttpEntity<>(counterPartyDetails, httpHeaders), TextNode.class);

      return ResponseEntity.ok(); //Return this to your front end
      }
}



private HttpHeaders generateHeadersForSignedRequest (
     HttpMethod httpMethod, String url, Object requestBody, PrivateKey privateKey)
     throws JsonProcessingException, SignatureException {

     String requestExpiryTime = String.valueOf(Instant.now().getEpochSecond() + 300);

     String textToSign = requestExpiryTime + "|" + httpMethod.name().toUpperCase() + "|" + url + "|";

     if (requestBody != null) {
         ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
         ObjectWriter objectWriter = objectMapper.writer();
         String requestJson = objectWriter.writeValueAsString(requestBody);
         textToSign += requestJson;
     }

     String signature = generateSignature(textToSign, privateKey);

     HttpHeaders httpHeaders = new HttpHeaders();
     httpHeaders.set("Signature", signature);
     httpHeaders.set("Expires-at", requestExpiryTime);

     return httpHeaders;
}

private String generateSignature(String plainText, PrivateKey privateKey)
    throws SignatureException {

    try {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(plainText.getBytes("UTF8"));
        byte[] valueSigned = signature.sign();

        return Base64.getEncoder().encodeToString(valueSigned);
    } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
        throw new SignatureException(e);
    }
}

The action of signing requires the merchant private key.The signature is valid until a given expiry time set by the merchant (max 10 minutes from the time the request is made).

The signature plain text is generated by concatenating the request components, each one separated by the '|' delimiter:

expiryTime | httpMethod | url | requestBody

Component Description
expiryTime Integer UNIX timestamp for signature expiry time
httpMethod String HTTP method (GET, POST etc) in upper case
url String URL of the endpoint
requestBody String representation of the JSON request body if present

An example of the signature plain text is as follows

1613639354|POST|https://api.paywithcitizen.com/v2/corporate-account/admin-counter-party"|{"customerIdentifier":"1846593725421829","bankCountry":"GB","accountGiro":"FPS","accountCurrency":"GBP","accountName":"Internal Account","accountNumber":"12345678","bankCode":"010203","counterPartyBank":"OPENPAYD","customerType":"CORPORATE"}
1613639354|POST|https://testapi.paywithcitizen.com/v2/corporate-account/admin-counter-party"|{"customerIdentifier":"1846593725421829","bankCountry":"GB","accountGiro":"FPS","accountCurrency":"GBP","accountName":"Internal Account","accountNumber":"12345678","bankCode":"010203","counterPartyBank":"OPENPAYD","customerType":"CORPORATE"}

The signature plain text is signed by an RSA 2048 bit key, the public part of which will have been registered on the Citizen service in advance. It is base 64 encoded and set in a header.

Webhooks

Webhooks are JSON objects that Citizen posts back to your merchant endpoints.

Configuring the webhooks you receive can be done in your merchant dashboard.

Webhook Signatures

All webhook updates will contain a signature of the transaction details which you can verify by the using of our public key. You get the public key with a simple GET call to /v2/merchant/citizen-signing-public-key

The signature can be found under the "Citizen-Signature" header of the request.

Verify Webhook Signature

Javascript: Verify Webhook Signature

  const citizenPublicKey =  "CITIZEN_PK";

  // data is the data key of the webhook payload in webhooks
  const verifySignature = (data, signature) => {
  const dataToVerify = JSON.stringify(data)

  //Convert the public key in base 64 (DER encoded) to array buffer
  let publicKeyAB = stringToArrayBuffer(atob(citizenPublicKey));

  const pubKey = crypto.createPublicKey({key: publicKeyAB, format: 'der', type: 'spki'})

  const verifySignatureFunction = crypto.createVerify('RSA-SHA256')
  verifySignatureFunction.update(dataToVerify, 'utf8');
  verifySignatureFunction.end()

  let verified = verifySignatureFunction.verify(pubKey, signature, "base64");
 }

Java: Verify Webhook Signature

String citizenPublicKey = "CITIZEN_PK";
String webhookData = "WEBHOOKDATA";
String webhookSignature = "WEBHOOKSIGNATURE";

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(citizenPublicKey));
PublicKey key = keyFactory.generatePublic(publicKeySpec);

Signature verifySignatureFunction = Signature.getInstance("SHA256withRSA");
verifySignatureFunction.initVerify(key);
verifySignatureFunction.update(webhookData.getBytes("utf-8"));

Boolean verified = verifySignatureFunction.verify(Base64.getDecoder().decode(webhookSignature));
  1. Get the Citizen Public Signing Key
    1. Production : https://api.paywithcitizen.com/v2/merchant/citizen-signing-public-key
    2. Test: https://testapi.paywithcitizen.com/v2/merchant/citizen-signing-public-key
  2. Decode the public key to UTF-8 from Base64
  3. Generate a public key using the decoded key extracted from step 2
  4. Create a Signature Verification function/object with algorithm RSA-SHA256
    1. Pass the generated public key
    2. Pass the Web Hook Data encoded as UTF-8
  5. Verify the result by passing the Webhook Signature

Pay-In Webhook Format

JSON: Pay-In Webhook Response

{
 "type":"<the event type of a webhook>",
 "data": {
    "citizenTransactionId": "<Citizen transaction id>",
    "merchantId": "<The id of your merchant>",
    "merchantTradingName": "<The trading name of your merchant>",
    "customerIdentifier": "<Customer id on your system>",
    "customerAccountNumber": "<the account number of the customer used in verified payin>",
    "customerBankCode": "<the bank code of the customer used in verified payin>",
    "customerName": "<the account holder name of the customer used in verified payin>",
    "version": "<Version of api>",
    "journeyType": "<Type of this pay-in journey. eg. EMAIL>",
    "transactionStatus": "<Current status of this transaction. eg. INITIATED>",
    "searchableText": "<A string that you can use to search for this pay-in>",
    "creationDate": "<Date of creation>",
    "paymentMethod": "<Method of payment, e.g. OPEN_BANKING>",
    "merchantAccountNumber": "<The account number of your merchant>",
    "merchantBankCode": "<The bank code of your merchant>",
    "reference": "<The reference given to the payin>",
    "paymentGiro": "<PaymentGiro, e.g. FPS>",
    "paymentAmount": "<The amount of the payment>",
    "paymentCurrency": "<The currency of the payment>",
    "description": "<The description given to the payin>",
    "customerEmailAddress": "<The customers email address>",
    "payload": "<the meta-data of the payin>",
    "payloadEncrypted": "<Boolean value indicated that the payload is encrypted or not>"
    }
}

Pay-In Transaction

Parameter Description
citizenTransactionId Citizen transaction id
merchantId The id of your merchant
merchantTradingName The trading name of your merchant
customerIdentifier Customer id on your system
customerAccountNumber Customer account number(this is filled only on verified payin)
customerBankCode Customer account number(this is filled only on verified payin)
customerName Customer account holder name(this is filled only on verified payin)
version Version of api
journeyType Type of this pay-in journey. eg. EMAIL
transactionStatus Current status of this transaction. eg. INITIATED
searchableText A string that you can use to search for this pay-in
creationDate Date of creation
paymentMethod Method of payment, e.g. OPEN_BANKING
paymentGiro PaymentGiro, e.g. FPS
paymentAmount The amount of the payment
paymentCurrency The currency of the payment
payload meta-data of the payment
payloadEncrypted Boolean value indicated that the payload is encrypted or not
merchantAccountNumber The account number of your merchant
merchantBankCode The bank code of your merchant
reference The reference given to the payin
description The description given to the payin
customerEmailAddress The customers email address

Pay-In Transaction Statuses

TransactionStatus Description
INITIATED The journey has begun and the user is about to select the bank he will pay from
PENDING_USER_AUTHORISATION The user has selected the bank he wants to pay from and has been redirected to his bank
ACCEPTED The pay-in has been authorised by the user and accepted by the financial provider
COMPLETE Pay-in transaction is completed and funds have settled
CANCELLED The user cancelled the pay-in
REJECTED_BY_ASPSP The pay-in has been rejected from the bank
FAILED The pay-in failed to be authorised by the bank
ERROR An error occurred during the pay-in journey. e.g INVALID_CONFIRMATION_CODE

Pay-In Webhook Status Types

Webhook Description Pay-In Transaction Status
PAYIN_CREATED Pay-In was created INITIATED
PAYIN_REDIRECT The user has selected a bank and been redirected to that bank PENDING_USER_AUTHORISATION
PAYIN_DECISION Pay-In decision was made by the user ACCEPTED or CANCELLED or REJECTED_BY_ASPSP or FAILED
PAYIN_ERROR An error occurred ERROR
PAYIN_COMPLETE The money have been credited to your account (only for corporate accounts) COMPLETE

Pay-In Completed Webhooks

Corporate accounts that are provided by Citizen can be used for pay in transactions. Leveraging that, we can offer even more accurate updates regarding the process of the transaction and notify you when the money have been credited to your account.

This functionality is required when a corporate account provided by citizen is used for pay in transactions.

Steps for using the above functionality

  1. Register a new webhook (PAYIN_COMPLETE) that will handle the updates of transaction.
  2. If you are an existing client and you would like to integrate the new functionality you will need to update your backend to handle the new status.

What is the main difference

Pay-In transactions that will leverage the new functionality will no longer end in the ACCEPTED stage but in the COMPLETE. Therefore, action may be required to be done in your backend. Furthermore, the transaction status history will contain that new state. In rare cases due to racing conditions, the transaction history might contain the COMPLETE status before the ACCEPTED. However, this would not affect the status of the transaction nor operation.

Account Verification Webhook Format

{
  "type":"<the event type of a webhook>",
  "data:": {
    "citizenTransactionId": "<Citizen transaction id>",
    "merchantId": "<The id of your merchant>",
    "merchantTradingName": "<The trading name of your merchant>",
    "customerIdentifier": "<Customer id on your system>",
    "financialServiceProvider": "<Financial Service Provider, e.g. BARCLAYS>",
    "version": "<Version of api>",
    "journeyType": "<Type of this account verification journey. eg. NO_EMAIL>",
    "transactionStatus": "<Current status of this transaction. eg. ACCEPTED>",
    "creationDate": "<Date of creation>",
    "accounts": [
      {
        "id": "<Id of the account>",
        "accountName": "<Name of the account (eg Current/Credit)>",
        "accountHolderName": "<Name of the accountholder>",
        "number": "<Encrypted bank account number (UK)>",
        "partialAccountNumber": "<Three digits of decrypted bank account number (UK)>",
        "sortCode": "<Bank account sort code (UK)>",
        "iban": "<Encrypted IBAN (EU)>",
        "partialIban": "<Three digits decrypted of IBAN (EU)>",
        "bic": "<Bank identifier (EU, eg Swift code)>",
        "balance": "<Balance of the account>",
        "type": "<Account type>",
        "details": "<Account information (JSON list of transactions)>",
        "bank": "<Bank of the account>"
      }
    ],
    "payload": "<the meta-data of the transaction>",
    "payloadEncrypted": "<Boolean value indicated that the payload is encrypted or not>"
  }
}

Account Verification Webhook parameters

Parameter Description
citizenTransactionId Citizen transaction id
merchantId The id of your merchant
merchantTradingName The trading name of your merchant
customerIdentifier Customer id on your system
financialServiceProvider financial Service Provider, e.g. BARCLAYS
version Version of api
journeyType Type of this pay-in journey. eg. EMAIL
transactionStatus Current status of this transaction. eg. INITIATED
searchableText A string that you can use to search for this pay-in
creationDate Date of creation
accounts Account object has account details only if transactionStatus is ACCEPTED
payload meta-data of the transaction
payloadEncrypted Boolean value indicated that the payload is encrypted or not

Account Object parameters

Parameter Description
id Id of the account
accountName Name of the account (eg Current/Credit)
accountHolderName Name of the accountholder
number Encrypted bank account number (UK)
partialAccountNumber Three digits of decrypted bank account number (UK)
sortCode Bank account sort code (UK)
iban Encrypted IBAN (EU)
partialIban Three digits decrypted of IBAN (EU)
bic Bank identifier (EU, eg Swift code)
balance Balance of the account
type Account type
details Account information (JSON list of transactions)
bank Bank of the account

Account verification Request Statuses

Account Verification Status Description
CREATED The journey has begun and the user is about to select the bank he want to verify the account from
PENDING_USER_AUTHORISATION The user has selected the bank he wants to verify and has been redirected to his bank
PENDING_ASPSP_ACCEPTANCE The request is processing from the bank
ACCEPTED The transaction has been completed
CANCELLED The transaction has been cancelled by the user
FAILED The transaction failed
ERROR An error occured processing the transaction

Account verification Webhook Status Types

Status Description Account Verification Status
ACCOUNT_VERIFICATION_CREATED Account verification journey was created CREATED
ACCOUNT_VERIFICATION_REDIRECT The user has selected a bank and been redirected to that bank PENDING_USER_AUTHORISATION
ACCOUNT_VERIFICATION_DECISION Account verification journey was completed ACCEPTED or CANCELLED or FAILED
ACCOUNT_VERIFICATION_ERROR An error occurred ERROR

Payout Webhook format

JSON: Payout Webhook Response

{
 "type":"<the event type of a webhook>",
 "data": {
     "citizenTransactionId": "<Citizen transaction id>",
     "merchantId": "<The id of your merchant>",
     "merchantTradingName": "<The trading name of your merchant>",
     "customerIdentifier": "<Customer id on your system>",
     "transactionStatus": "<The payout status>",
     "creationDate": "<Date of creation>",
     "citizenCounterPartyId": "<The id of counter party>",
     "currency": "<The currency of the payout>",
     "paymentGiro": "<PaymentGiro, e.g. FPS>",
     "merchantBank": "<The bank of the merchant>",
     "counterPartyBank": "<The bank of the customer that requests the payout>",
     "counterPartyAccountName": "<The Account Name of Counter Party>",
     "amount": "<The amount of the payout>",
     "reference": "<The reference of the payout>",
     "payload": "<the meta-data of the payout>",
     "payloadEncrypted": "<Boolean value indicated that the payload is encrypted or not>",
     "counterPartyBankCode": "<The Bank Code of Counter Party>",
     "counterPartyAccountNumber": "<The Account Number of Counter Party>"
    }
}

Payout transaction

Parameter Description
citizenTransactionId Citizen transaction id
merchantId The id of your merchant
merchantTradingName The trading name of your merchant
customerIdentifier Customer id on your system
transactionStatus The payout status
creationDate Date of creation
citizenCounterPartyId The id of counter party
currency The currency of the payout
paymentGiro PaymentGiro, e.g. FPS
merchantBank The bank of the merchant
counterPartyBank The bank of the customer that requests the payout
counterPartyBankCode The Bank Code of Counter Party
counterPartyAccountNumber The Account Number of Counter Party
counterPartyAccountName The Account Name of Counter Party
searchableText A string that you can use to search for this payout
amount The amount of the payout
reference The reference of the payout
payload meta-data of the payout
payloadEncrypted Boolean value indicated that the payload is encrypted or not

Payout transaction statuses

Status Description
CREATED The payout has been initialised
QUEUED The payout has been added to a queue
COMPLETE The payout has been submitted to the bank
ERROR The payout failed to be submitted
FAILED The payout has been already accepted and refund
CANCELLED_BY_MERCHANT The queued payout has been rejected by the merchant

Payout Webhook status types

Webhook Description Payout Transaction Status
PAYOUT_CREATED Payout was created CREATED
PAYOUT_ACCEPTED Payout was submitted to the bank COMPLETE
PAYOUT_QUEUED Payout was added to queue QUEUED
PAYOUT_ERROR An error occurred ERROR, FAILED
PAYOUT_REJECTED Payout that was in queue was rejected CANCELLED_BY_MERCHANT

Migrating from V1 to V2

Migrating to version 2 of the Citizen service requires setting up a new merchant account moving your payment settings to it. You can continue to use your version 1 integration until your version 2 integration is tested and ready to move to.

There have been a few changes to terminology. What was previosly called an 'Entity' in version 1 is referred to as a 'Merchant' for version 2. Similarly, a 'token' is now called a 'transaction'. Transaction types have also been updated - 'payments' are now called 'pay-ins' and 'Account Information Services' are now called 'Account Verification'.

Version 1 has two identifiers for a transaction -- a 'token' ID and a 'transaction' ID. Version 2 combines these into a single identifier, named 'CitizenTransactionId. This simplifies fields in the various journeys for account verificiation, pay-ins and payouts. It is also more straightforward to find transactions -- the 'CitizenTransactionId' follows the transaction from session creation through to journey completion.

The structure of the various pay-in, payout and account verification journeys is broadly the same as the V1 journeys. Most changes are in field names and path variables. Key changes are in the table below. Changes for each journey are also covered its relevant section in the API documentation.

V1 Field V2 Field
entityId merchantId
merchantInternalId customerIdentifier
hashedRequesterEmail merchantId
requesterUsername merchantTradingName
hashedUserEmail customerIdentifier
merchantInternalId customerIdentifier
tokenStatus transactionStatus

Account Verification Journey

The structure of this journey is essentially the same as a V1 journey, however a few details have been updated. A temporary 'session' is first created using your merchant private API key from your back end. A permanent 'transaction' is the created from your front end using your merchant public API key. As before this gives a redirect from which the customer can authorise sharing of details with their bank. After the authorisation, details of the account verification transaction may be fetched from your back end using your merchant private API key.

Session creation

The first step in the account verification journey is creating a temporary 'session'. This step is is made in both V1 and V2 of the API.

cURL: V1 create account verification session

curl https://api.paywithcitizen.com/v1/ais/initialise-session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "customerEmailAddress":"customer@gmail.com", \
        "merchantEmailAddress":"payments@merchant.com", \
        "aisScope":"account_details" \
      }'
curl https://testapi.paywithcitizen.com//v1/ais/initialise-session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "customerEmailAddress":"customer@gmail.com", \
        "merchantEmailAddress":"payments@merchant.com", \
        "aisScope":"account_details" \
      }'

response: V1 create account verification session

<aisSessionId>

cURL: V2 create account verification session

curl https://api.paywithcitizen.com/v2/account-verification/session \
  -X POST \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{
    "customerIdentifier":<ID such as an email address or phone number>,
    "merchantId":<your merchant ID>,
    "scopes":["account_details"]  
   }'
curl https://testapi.paywithcitizen.com/v2/account-verification/session \
  -X POST \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{
    "customerIdentifier":<ID such as an email address or phone number>,
    "merchantId":<your merchant ID>,
    "scopes":["account_details"]  
   }'

response: V2 create account verification session

<citizenTransactionId>
V1 Endpoint V2 Endpoint
/v1/ais/initialise-session /v2/account-verification/session
V1 Request Field V2 Request Field
customerEmailAddress customerIdentifier
merchantEmailAddress merchantId
aisScope scopes
V1 Response Field V2 Response Field
Session ID (Text Node) Citizen Transaction ID (String)





















Transaction creation

If the user wishes to proceed then a permanent 'transaction' is created using your company public API key from your front end. This call returns an redirect URL from which a customer can authorise the transaction with their bank. The call is made in both V1 and V2 of the API.

cURL: V1 create account verification transaction

curl https://api.paywithcitizen.com/v1/ais/initialise-no-email \
  -X POST \
  -H AuthorizationCitizen: <your company public api key> \
  -d '"<Session ID>"'
curl https://testapi.paywithcitizen.com/v1/ais/initialise-no-email \
  -X POST \
  -H AuthorizationCitizen: <your company public api key> \
  -d '"<Session ID>"'

response: V1 create account verification transaction

<Authorisation redirect URL>

cURL: V2 create account verification transaction

curl https://api.paywithcitizen.com/v2/account-verification/initialise \
  -X POST \
  -H AuthorizationCitizen: <your company public api key> \
  -d '<citizenTransactionId>'

curl https://testapi.paywithcitizen.com/v2/account-verification/initialise \
  -X POST \
  -H AuthorizationCitizen: <your company public api key> \
  -d '<citizenTransactionId>'

response: V2 create account verification transaction

<Authorisation redirect URL>
V1 Endpoint V2 Endpoint
/v1/ais/initialise-no-email /v2/account-verification/initialise
V1 Request Field V2 Request Field
Session ID (Text Node) Citizen Transaction ID (String)





















Fetch the customer bank account details

The transaction can be fetched at any stage after its creation. This example shows a transaction that has gone through the journey, been authorised by the user and accepted by their bank.

cURL: V1 fetch account verification transaction

curl https://api.paywithcitizen.com/v1/ais/entity/<entityId>/<tokenId> \
  -X GET \
  -H AuthorizationCitizen: <your company private api key> \
  -H X-code: <your company secret>
curl https://testapi.paywithcitizen.com/v1/ais/entity/<entityId>/<tokenId> \
  -X GET \
  -H AuthorizationCitizen: <your company private api key> \
  -H X-code: <your company secret>

response: V1 fetch account verification transaction

  {
    "id":<token ID>,
    "version":"2",
    "journeyType":"NO_EMAIL",
    "tokenType":"BANK_ACCOUNT_INFORMATION",
    "tokenStatus":"GRANTED",
    "hashedUserEmail":"c41e5c4edecb6977e47f6efb1ca6a959a9e3480046d38151daf8b51fc754892f",
    "encryptedUserEmail":"/WaMLxGyK61RCAHI6r/8TCr9Yz69mPLOKlXfJS/8CQ==",
    "duration":0,
    "access":128,
    "decisionDate":1649932266242, 
    "decisionHashedEmail":"c41e5c4edecb6977e47f6efb1ca6a959a9e3480046d38151daf8b51fc754892f",
    "hashedRequesterEmail":"8c37b3d061e455eeded3126e1a41765e794783138f67bf35a9d11d150dc5b206",
    "requesterUsername":"My Company",
    "creationDate":1649932264120,
    "needsManualReview":false,
    "payloadEncrypted":false,
    "citizenTransactionId":"b79742e3-24d2-01a7-5adb-dd2203",
    "financialServiceProvider":"CITIZEN_TEST_BANK",
    "aisRequestStatus":"ACCEPTED",
    "accounts": [
      {
        "id":"y3CZ2cX7013oNS5ofE2p",
        "accountName":"Main Account",
        "accountHolderName":"John Doe",
        "number":"57583676",
        "partialAccountNumber":"676",
        "sortCode":"040075",
        "iban":"GB71REVO00997094014496",
        "partialIban":"496",
        "bic":"REVOGB21",
        "type":"account",
        "details":"{\"client_name\":\"John Doe\",\"account_number\":\"57583676\",\"sort_code\":\"040075\",\"iban\":\"GB71REVO00997094014496\",\"bic\":\"REVOGB21\"}",
        "bank":"CITIZEN_TEST_BANK"
      }
    ]
  }

cURL: V2 fetch account verification transaction

curl https://api.paywithcitizen.com/v2/account-verification/<cititzenTransactionId> \
  -X GET \
  -H AuthorizationCitizen: <your company private api key>
curl https://testapi.paywithcitizen.com/v2/account-verification/<cititzenTransactionId> \
  -X GET \
  -H AuthorizationCitizen: <your company private api key> 

response: V2 fetch account verification transaction

  {
    "citizenTransactionId":<citizenTransactionId>,
    "merchantId":<your merchant ID>,
    "merchantTradingName":"Merchant Ltd",
    "customerIdentifier":"customer@gmail.com",
    "financialServiceProvider":"CITIZEN_TEST_BANK",
    "version":"2",
    "journeyType":"NO_EMAIL",
    "transactionType":null,
    "transactionStatus":"ACCEPTED",
    "searchableText":null,
    "payload":null,
    "creationDate":1649932264120,
    "testParameters":{},
    "accounts": [
      {
        "id":"a4FNxeFUJLxVMd33FmLK",
        "accountName":"Main Account",
        "accountHolderName":"John Doe",
        "number":"57583676",
        "partialAccountNumber":"676",
        "sortCode":"040075",
        "iban":"GB71REVO00997094014496",
        "partialIban":"496",
        "bic":"REVOGB21",
        "balance":"null",
        "type":"account",
        "details":"{\"client_name\":\"John Doe\",\"account_number\":\"57583676\",\"sort_code\":\"040075\",\"iban\":\"GB71REVO00997094014496\",\"bic\":\"REVOGB21\"}",
        "bank":"CITIZEN_TEST_BANK"
      }
    ],
    "payloadEncrypted":false
  }
V1 Headers V2 Headers
X-code N/A
V1 Endpoint V2 Endpoint
/v1/ais/entity/<entityId>/<tokenId> /v2/account-verification/<cititzenTransactionId>
V1 Request Field V2 Request Field
Token ID Citizen Transaction ID
Entity ID N/A
V1 Response Field V2 Response Field
entityId merchantId
id citizenTransactionId
requesterUsername merchantTradingName
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
tokenStatus transactionStatus



















































Verified Pay In Journey

This section covers a 'No Email' Verified Pay In journey. Again, the journey structure is essentially the same as a V1 journey with changes to a few details. A temporaryPay In 'session' is first created using your merchant private API key from your back end.

A permanent 'transaction' is the created from your front end using your merchant public API key. As before this gives a redirect from which the customer can authorise sharing of details with their bank. After the authorisation, details of the account verification transaction may be fetched from your back end using your merchant private API key.

Session creation

The first step in the pay-in journey is creating a temporary 'session'. This step is is made in both V1 and V2 of the API.

cURL: V1 create verified pay-in session

curl https://api.paywithcitizen.com/v1/payments/verified-deposit-session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "paymentGiro":"FPS",
        "customerEmailAddress":<customer email address>,
        "merchantEmailAddress":<your company email address>,
        "merchantInternalId":<merchant identifier for the customer>,
        "amount":"1.02",
        "currency":"GBP",
        "shortReference":"TestShortRef",
        "detailedReference":"Test payment for Citizen app",
        "customerIpAddress":"192.168.1.2",
        "customerDeviceOs":"Android"
      }'
curl https://testapi.paywithcitizen.com/v1/payments/verified-deposit-session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "paymentGiro":"FPS",
        "customerEmailAddress":<customer email address>,
        "merchantEmailAddress":<your company email address>,
        "merchantInternalId":<merchant identifier for the customer>,
        "amount":"1.02",
        "currency":"GBP",
        "shortReference":"TestShortRef",
        "detailedReference":"Test payment for Citizen app",
        "customerIpAddress":"192.168.1.2",
        "customerDeviceOs":"Android"
      }'

response: V1 create verified pay-in session

  <verified pay-in session ID>

cURL: V2 create verified pay-in session

curl https://api.paywithcitizen.com/v2/payins/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "customerIdentifier":<customer ID, eg phone number or email address>,
        "paymentGiro":"FPS",
        "amount":"1.02",
        "currency":"GBP",
        "reference":"TestReference",
        "customerIpAddress":"192.168.1.2",
        "customerDeviceOs":"Android"
      }'
curl https://testapi.paywithcitizen.com/v2/payins/verified/session \
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key> \
  -d '{ \
        "customerIdentifier":<customer ID, eg phone number or email address>,
        "paymentGiro":"FPS",
        "amount":"1.02",
        "currency":"GBP",
        "reference":"TestReference",
        "customerIpAddress":"192.168.1.2",
        "customerDeviceOs":"Android"
      }'

response: V2 create verified pay-in session

  <Citizen transaction ID>
V1 Endpoint V2 Endpoint
/v1/payments/verified-deposit-session /v2/payins/verified/session
V1 Request Field V2 Request Field
merchantEmailAddress N/A
merchantInternalId customerIdentifier
shortReference reference
detailedReference reference
V1 Response Field V2 Response Field
Session ID (Text Node) Citizen Transaction ID (String)


























Get Relevant Verified Bank Accounts

The customer may use a bank account that they have already verified for the merchant through Citizen. The following call fetches such verified bank accounts if they are present. If no suitable verified bank accounts are found, an account verification session can be created so the customer can add a bank account, which is covered in the next section.

cURL: V1 get verified bank accounts

curl https://api.paywithcitizen.com/v1/ais/verified-deposit-accounts-and-details/<sessionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
curl https://testapi.paywithcitizen.com/v1/ais/verified-deposit-accounts-and-details/<sessionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>

response: V1 get verified bank accounts

  {
    "paymentTransactionDetails": {
      "citizenPaymentId":"99892072-4c81-b981-ef9b-953ef4",
      "amount":"1.02",
      "currency":"GBP",
      "shortReference":"TestPayment",
      "bankAccountNumber":"57583676",
      "bankCode":"04-00-75",
      "payloadEncrypted":false
    },
    "accounts": [
      {
        "id":"625412ef6b22db36ff03d3ae",
        "version":"2",
        "journeyType":"VERIFIED_DEPOSIT_NO_EMAIL",
        "tokenType":"BANK_ACCOUNT_INFORMATION",
        "tokenStatus":"GRANTED",
        "hashedUserEmail":"f3765b83f05ed7f13a31cdfbae55dd911f348a7acc7c8e17808e4e4d3904fd89",
        "encryptedUserEmail":"/pMCwJs4C+gADg0t8qyS855EqeiuaXkAsGkcYvuYVYzYkyA7",
        "duration":0,
        "access":128,
        "decisionDate":1649677042053,
        "decisionHashedEmail":"f3765b83f05ed7f13a31cdfbae55dd911f348a7acc7c8e17808e4e4d3904fd89",
        "hashedRequesterEmail":"c4cf9e2762dd82c4a0e31621904e83661fe2130e3e7c8d760f891d6bde5f1a30",
        "requesterUsername":"Merchant Ltd",
        "creationDate":1649677039824,
        "needsManualReview":false,
        "payloadEncrypted":false,
        "merchantInternalId":"customer@gmail.com",
        "citizenTransactionId":"736087d4-2a8b-b53e-6cba-1a342e",
        "financialServiceProvider":"CITIZEN_TEST_BANK",
        "aisRequestStatus":"ACCEPTED",
        "accounts": [
          {
            "id":"x6yGpGecvPrp320C4mjB",
            "accountName":"Main Account",
            "accountHolderName":"yZ8F3cwoFq1daU4YppGN5YRHgfiV64+DOn4xwjfPXa7hQGSlwvjceus=",
            "number":"v8FEjIV7T7sLb5tn/11S7W3oSRbrapA5",
            "partialAccountNumber":"676",
            "sortCode":"usJBhIF4n0YrVTyT5K1f0p88AM7bGQ==",
            "iban":"zbRGheQILsJCfld9sfXRosBb9K/PsrUrwFcR3HS+AUXjzuwYko0=",
            "partialIban":"496",
            "bic":"2LMn+/EPSrxCmTg40Glg9H//DwITb7iS",
            "type":"account",
            "details":"8dQS2N8oFvktIA8p4+fStLMDtPKM4ZTNYbpup1vahw5zXxwgkJczmIbM5XqWEnUP/nNVAVy3ofFg9buCB/slf5975jTfDQrwcEH6A8l7zAgHIz6ZTyi/GuO5fbgaYiPZS539rwZtPpiqulPAG3nQl7QH80KZrxXyuy5/cH115jKYWYsq7PdcHL7wAYGLIsfX3CaLfkvncva8uLI8Zk8=",
            "bank":"CITIZEN_TEST_BANK"
          }
        ]
      }
    ]
  }

cURL: V2 get verified bank accounts

curl https://api.paywithcitizen.com/v2/account-verification/verified-payin/accounts/<citizenTransactionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payin/accounts/<citizenTransactionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>

response: V2 get verified bank accounts

  {
    "accountTransactions": [
      {
        "citizenTransactionId":<citizenTransactionId>,
        "merchantId":<your merchant ID>,
        "merchantTradingName":"Merchant Ltd",
        "customerIdentifier":"customer@gmail.com",
        "financialServiceProvider":"CITIZEN_TEST_BANK",
        "version":"2",
        "journeyType":"VERIFIED_PAYIN_NO_EMAIL",
        "transactionStatus":"ACCEPTED",
        "creationDate":1649932264120,
        "testParameters":{},
        "accounts": [
          {
            "id":"gF25x6Tcl41xR6DcsVHp",
            "accountName":"Main Account",
            "accountHolderName":"John Doe",
            "partialAccountNumber":"676",
            "partialIban":"496",
            "type":"account",
            "bank":"CITIZEN_TEST_BANK"
          }
        ],
        "payloadEncrypted":false
      }
    ],
    "paymentTransactionDetails": {
      "citizenTransactionId":<citizenTransactionId>,
      "customerIdentifier":"customer@gmail.com",
      "paymentGiro":"FPS",
      "amount":"1.02",
      "currency":"GBP",
      "reference":"TestPayment",
      "customerIpAddress":"192.168.1.2",
      "customerDeviceOs":"Android",
      "testParameters":{},
      "payloadEncrypted":false
    }
  }
V1 Endpoint V2 Endpoint
/v1/ais/verified-deposit-accounts-and-details/<sessionId> /v2/account-verification/verified-payin/accounts/<sessionId>
V1 Response Field V2 Response Field
citizenPaymentId citizenTransactionId
merchantInternalId customerIdentifier
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
requesterUsername merchantTradingName






















































































Adding a verified bank account

If the customer does not have any verified bank accounts with which they can make the pay-in one needs to be added before they can complete a verified pay-in. There is a difference at this step in the journey -- V1 of the API uses a single REST call to create an account verification 'session' and 'transaction', V2 uses a REST call to create the 'session' and another to create the 'transaction'.

cURL: V1 create account verification session and transaction

curl https://api.paywithcitizen.com/v1/ais/initialise-verified-deposit-no-email
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '"<verified pay-in session ID>"'
curl https://testapi.paywithcitizen.com/v1/ais/initialise-verified-deposit-no-email
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '"<verified pay-in session ID>"'

response: V1 create account verification session and transaction

<authorisation redirect URL>

cURL: V2 create account verification session

curl https://api.paywithcitizen.com/v2/account-verification/verified-payin/add-bank-accounts
  -X PUT \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payin/add-bank-accounts
  -X PUT \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'

response: V2 create account verification session

  <citizenTransactionId>

cURL: V2 create account verification transaction

curl https://api.paywithcitizen.com/v2/account-verification/initialise-verified-payin
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'
curl https://testapi.paywithcitizen.com/v2/account-verification/initialise-verified-payin
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'

response: V2 create account verification transaction

  <authorisation redirect URL>
V1 Endpoint V2 Endpoint
/v1/ais/initialise-verified-deposit-no-email /v2/account-verification/verified-payin/add-bank-accounts
/v2/account-verification/initialise-verified-payin
V1 Request Field V2 Request Field
Session ID (Text Node) Citizen Transaction ID (String)































Create a pay-in transaction

If the user wishes to proceed with the pay-in then a permanent 'transaction' is created using your company public API key from your front end. This call returns an redirect URL from which a customer can authorise the transaction with their bank. The call is made in both V1 and V2 of the API

cURL: V1 create pay-in transaction

curl https://api.paywithcitizen.com/v1/payments/initialise-verified-deposit-no-email
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '{
        "sessionId":"<verified pay-in session ID>",
        "customerBankAccountId":"<customer bank account ID>"
      }'

curl https://testapi.paywithcitizen.com/v1/payments/initialise-verified-deposit-no-email
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '{
        "sessionId":"<verified pay-in session ID>",
        "customerBankAccountId":"<customer bank account ID>"
      }'

cURL: V1 create pay-in transaction

  <authorisation redirect URL>

cURL: V2 create pay-in transaction

curl https://api.paywithcitizen.com/v2/payins/verified/initialise
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '<citizenTransactionId>'
curl https://testapi.paywithcitizen.com/v2/payins/verified/initialise
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '<citizenTransactionId>'

cURL: V2 create pay-in transaction

  <authorisation redirect URL>
V1 Endpoint V2 Endpoint
v1/payments/initialise-verified-deposit-no-email /v2/payins/verified/initialise
V1 Request Field V2 Request Field
customerBankAccountId N/A


























Fetch a pay-in transaction

The transaction can be fetched at any stage after its creation. This example shows a transaction that has gone through the journey, been authorised by the user and accepted by their bank.

cURL: V1 fetch pay-in transaction

curl https://api.paywithcitizen.com/v1/payments/entity/<payment token ID>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>
curl https://testapi.paywithcitizen.com/v1/payments/entity/<payment token ID>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>

response: V1 fetch pay-in transaction

  {
      "id":"625413016b22db36ff03d3af",
      "metaData":{},
      "version":"2",
      "name":"Merchant Ltd",
      "journeyType":"VERIFIED_DEPOSIT_NO_EMAIL",
      "tokenType":"PAYMENT",
      "tokenStatus":"GRANTED",
      "hashedUserEmail":"f3765b83f05ed7f13a31cdfbae55dd911f348a7acc7c8e17808e4e4d3904fd89",
      "duration":0,
      "access":128,
      "hashedRequesterEmail":"c4cf9e2762dd82c4a0e31621904e83661fe2130e3e7c8d760f891d6bde5f1a30",
      "requesterUsername":"Merchant Ltd",
      "creationDate":1649677057266,
      "creationIpAddress":"192.168.1.2",
      "needsManualReview":false,
      "payloadEncrypted":false,
      "merchantInternalId":"customer@gmail.com",
      "paymentProvider":"CITIZEN_TEST_BANK",
      "paymentMethod":"OTHER",
      "paymentGiro":"FPS",
      "citizenPaymentId":"99892072-4c81-b981-ef9b-953ef4",
      "paymentStatus":"ACCEPTED",
      "customerEmailAddress":"customer@gmail.com",
      "customerAccountNumber":"57583676",
      "customerBankCode":"040075",
      "merchantEmailAddress":"payments@merchant.com",
      "merchantAccountNumber":"55026646",
      "merchantBankCode":"050185",
      "paymentAmount":1.02,
      "paymentCurrency":"GBP",
      "shortReference":"TestShortRef",
      "detailedReference":"Test payment reference",
      "accountInformationId":"625412ef6b22db36ff03d3ae"
    }

cURL: V2 fetch pay-in transaction

curl https://api.paywithcitizen.com/v2/payins/<citizenTransactionId>
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>
curl https://testapi.paywithcitizen.com/v2/payins/<citizenTransactionId>
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>

response: V2 fetch pay-in transaction

  {
    "citizenTransactionId":<citizenTransactionId>,
    "merchantId":<your merchant ID>,
    "merchantTradingName":"Merchant Ltd",
    "customerIdentifier":"customer@gmail.com",
    "version":"2",
    "journeyType":"VERIFIED_PAYIN_NO_EMAIL",
    "transactionStatus":"ACCEPTED",
    "creationDate":1649677057266,
    "testParameters":{},
    "paymentMethod":"OTHER",
    "paymentGiro":"FPS",
    "merchantAccountNumber":"57583676",
    "merchantBankCode":"04-00-75",
    "paymentAmount":1.02,
    "paymentCurrency":"GBP", 
    "paymentProvider":"CITIZEN_TEST_BANK",
    "payloadEncrypted":false
  }
V1 Endpoint V2 Endpoint
/v1/payments/entity/<payment token ID> /v2/payins/<pay-in session ID>
V1 Request Field V2 Request Field
Token ID Pay-in Session ID
V1 Response Field V2 Response Field
id citizenTransactionId
hashedRequesterEmail merchantId
requesterUsername merchantTradingName
hashedUserEmail customerIdentifier
merchantInternalId customerIdentifier
tokenStatus transactionStatus





















Verified Payout Journey

This section covers a Verified Payout journey. Again, the journey structure is essentially the same as a V1 journey with changes to a few details. A temporaryPay In 'session' is first created using your merchant private API key from your back end. The session details are signed with your private RSA key. The details of signing the request have not changed for V2.

A permanent 'transaction' is the created from your front end using your merchant public API key. As before this gives a redirect from which the customer can authorise sharing of details with their bank. After the authorisation, details of the account verification transaction may be fetched from your back end using your merchant private API key.

Session creation

The first step in the pay-in journey is creating a temporary 'session'. This step is is made in both V1 and V2 of the API.

cURL: V1 create verified payout session

curl https://api.paywithcitizen.com/v1/payouts/verified-payout-session
    -X POST \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>
    -H Signature: <request signature>
    -H Expires-at: <request expiry>
    -d '{
          "customerEmailAddress":<customer email address>,
          "merchantEmailAddress":<your company email address>,
          "merchantInternalId":<merchant identifier for the customer>,
          "currency":"GBP",
          "paymentGiro":"FPS",
          "merchantBankCode":"608384",
          "merchantAccountNumber":"40074844",
          "amount":0.11,
          "reference":"TestPayout",
          "customerIpAddress":"192.168.1.2"
        }'
curl https://testapi.paywithcitizen.com/v1/payouts/verified-payout-session
    -X POST \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>
    -H Signature: <request signature>
    -H Expires-at: <request expiry>
    -d '{
          "customerEmailAddress":<customer email address>,
          "merchantEmailAddress":<your company email address>,
          "merchantInternalId":<merchant identifier for the customer>,
          "currency":"GBP",
          "paymentGiro":"FPS",
          "merchantBankCode":"608384",
          "merchantAccountNumber":"40074844",
          "amount":0.11,
          "reference":"TestPayout",
          "customerIpAddress":"192.168.1.2"
        }'

response: V1 create verified payout session

  <verified payout session ID>

cURL: V2 create verified payout session

curl https://api.paywithcitizen.com/v2/payouts/verified/session
    -X POST \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>
    -H Signature: <request signature>
    -H Expires-at: <request expiry>
    -d '{
          "customerIdentifier":<customer ID, eg phone number or email address>,
          "merchantId":<your merchant ID>,
          "currency":"GBP",
          "paymentGiro":"FPS",
          "amount":10.00,
          "reference":"TestPayment",
          "customerIpAddress":"192.168.1.2",
          "customerDeviceOs":"iOS 14"
        }'
curl https://testapi.paywithcitizen.com/v2/payouts/verified/session
    -X POST \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company private api key>
    -H Signature: <request signature>
    -H Expires-at: <request expiry>
    -d '{
          "customerIdentifier":<customer ID, eg phone number or email address>,
          "merchantId":<your merchant ID>,
          "currency":"GBP",
          "paymentGiro":"FPS",
          "amount":10.00,
          "reference":"TestPayment",
          "customerIpAddress":"192.168.1.2",
          "customerDeviceOs":"iOS 14"
        }'

response: V2 create verified payout session

  <citizenTransactionId>
V1 Endpoint V2 Endpoint
/v1/payouts/verified-payout-session /v2/payouts/verified/session
V1 Request Field V2 Request Field
customerEmailAddress customerIdentifier
merchantEmailAddress merchantId
V1 Response Field V2 Response Field
session ID (Text Node) Citizen Transaction ID (String)




































Get Relevant Verified Bank Accounts

he customer may use a bank account that they have already verified for the merchant through Citizen. The following call fetches such verified bank accounts if they are present. If no suitable verified bank accounts are found, an account verification session can be created so the customer can add a bank account, which is covered in the next section.

cURL: V1 get verified bank accounts

curl https://api.paywithcitizen.com/v1/ais/verified-payout-accounts-and-details/<verified payout session ID>
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>
curl https://testapi.paywithcitizen.com/v1/ais/verified-payout-accounts-and-details/<verified payout session ID>
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>

response: V1 get verified bank accounts

  {
    "payoutTransactionDetails": {
      "citizenPaymentId":"zSqEFpPBqaExStaPw7",
      "merchantEmailAddress":"5cIjiySmbPehGko8QECiJCaik+J4rhJswft38fqnCURe",
      "counterPartyId":null,
      "merchantInternalId":"3335645495054",
      "currency":"GBP",
      "paymentGiro":"FPS",
      "merchantBankCode":"lpNowXb3/pp1t/jyOkSAnR0h7mx+GA==",
      "merchantAccountNumber":"lJNgxXr7LIOL/Mrj1vGG3oHZKWzXh23v",
      "merchantBank":"CITIZEN_TEST_BANK",
      "amount":0.11,
      "reference":"Test payout 01",
      "customerIpAddress":"kZpi3H/1IJn1WRkJw9b5aQsDNlJzYizKdrQC",
      "payloadEncrypted":false,
      "queue":false,
    },
    "accounts": [
      {
        "id":"62542a4ba78d5c64d79bea8b",
        "version":"2",
        "journeyType":"VERIFIED_PAYOUT_NO_EMAIL",
        "tokenType":"BANK_ACCOUNT_INFORMATION",
        "tokenStatus":"GRANTED",
        "hashedUserEmail":"bc6cab45fbf15482be30e221d2be29e47e57a54505c12e25328b5d03531153db",
        "encryptedUserEmail":"btna0Or7ZE7QTYKVnPTxCUcHjy5G6S3uPc5tURR60Bg=",
        "duration":0,
        "access":128,
        "decisionDate":1649683021262,
        "decisionHashedEmail":"bc6cab45fbf15482be30e221d2be29e47e57a54505c12e25328b5d03531153db",
        "hashedRequesterEmail":"f081c929520016e50a472d2f0b9afda6e0034d65ec5e9128aec7a1623b7e4ba0",
        "requesterUsername":"Merchant",
        "creationDate":1649683019057,
        "needsManualReview":false,
        "payloadEncrypted":false,
        "merchantInternalId":"test1@citizen.is",
        "citizenTransactionId":"77ba1550-175e-3bcf-f7bb-23151d",
        "financialServiceProvider":"CITIZEN_TEST_BANK",
        "aisRequestStatus":"ACCEPTED",
        "accounts": [
          {
            "id":"anrqEL1v2cmtGl0xJGoJ",
            "accountName":"Main Account",
            "accountHolderName":"WdXdzaHeaQeLA9is0o79CTx+MymScq9KW/eFYZteMKj89c1DHhajDTs=",
            "number":"L4ucnOiNMBFVH1/bVnGZNNulgg90mNp8",
            "partialAccountNumber":"676",
            "sortCode":"KoiZlOyOhFeGs62H/PA5AxmIIiTFmw==",
            "iban":"Xf6elYn+UWiUFMHJxeqhTnhiRn7IK5V57WIH5IUcsu4UPbI8A44=",
            "partialIban":"496",
            "bic":"SPn/65z5NRbLQQ20Z2MuRroC6TGy4Y72",
            "type":"account",
            "details":"YZ7KyLLeaVP7Spmdl/iiWAs6BiOLeLQEAKqj0+bnVwDRvNiXIvoNaAYOkjhBMgyVvIhbkFDbq5q6MzSiNmhCv1daSuTu5ox2xoo4daeoNKz+cRg5t9hchnWdEDX5qQhr8Me8G4E5hffxv3zcNlRhSQYrm3fIu+EurF3jG3GjNOAbOlf9tK2m5qugeGkHGKO5IGnNyKL1epK/pXLcU1o=",
            "bank":"CITIZEN_TEST_BANK"
          }
        ]
      }
    ]
  }

cURL: V2 get verified bank accounts

curl https://api.paywithcitizen.com/v2/account-verification/verified-payout/accounts/<citizenTransactionId>
   -X GET \
   -H Content-Type: application/json \
   -H AuthorizationCitizen: <your company public api key>
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payout/accounts/<citizenTransactionId>
   -X GET \
   -H Content-Type: application/json \
   -H AuthorizationCitizen: <your company public api key>

response: V2 get verified bank accounts

  {
    "accountTransactions": [
      {
        "citizenTransactionId":"f38GfdWRlc5ReCfEw9", 
        "merchantId":"625536e7866f387953ca3e5a",
        "merchantTradingName":"Merchant Ltd",
        "customerIdentifier":"customer@gmail.com",
        "financialServiceProvider":"CITIZEN_TEST_BANK",
        "version":"2",
        "journeyType":"VERIFIED_PAYOUT_NO_EMAIL",
        "transactionStatus":"ACCEPTED",
        "creationDate":1649683019057,
        "testParameters":{},
        "accounts": [ 
          {
            "id":"t5fD39FVfg02ffVLfcvG",
            "accountName":"Main Account",
            "accountHolderName":"John Doe",
            "partialAccountNumber":"676",
            "partialIban":"496",
            "type":"account",
            "bank":"CITIZEN_TEST_BANK"
          }
        ],
        "payloadEncrypted":false
      }
    ],
    "payoutTransactionDetails": {
      "citizenTransactionId":"fGf53GbCh8GQ4f4f5T",
      "customerIdentifier":"customer@gmail.com",
      "merchantId":"625536e7866f387953ca3e5a",
      "currency":"GBP",
      "paymentGiro":"FPS",
      "amount":10.00,
      "reference":"TestPayment",
      "customerIpAddress":"192.168.1.2",
      "customerDeviceOs":"iOS 14",
      "customerAccountHolderName":"John Doe",
      "payloadEncrypted":false,
      "supportedCountries":[],
      "queue":false,
      "testParameters":{}
    }
  }
V1 Endpoint V2 Endpoint
/v1/ais/verified-payout-accounts-and-details/<verified payout session ID> /v2/account-verification/verified-payout/accounts/<Citizen Transaction ID>
V1 Response Field V2 Response Field
citizenPaymentId citizenTransactionId
merchantInternalId customerIdentifier
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
requesterUsername merchantTradingName



























































































Adding a verified bank account

If the customer does not have any verified bank accounts with which they can make the payout one needs to be added before they can complete a verified payout. There is a difference at this step in the journey -- V1 of the API uses a single REST call to create an account verification 'session' and 'transaction', V2 uses a REST call to create the 'session' and another to create the 'transaction'.

cURL: V1 create account verification session and transaction

curl https://api.paywithcitizen.com/v1/ais/initialise-verified-payout-no-email
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>
    -d '"<session ID>"'
curl https://testapi.paywithcitizen.com/v1/ais/initialise-verified-payout-no-email
    -X GET \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>
    -d '"<session ID>"'

response: V1 create account verification session and transaction

<authorisation redirect URL>

cURL: V2 create account verification session

curl https://api.paywithcitizen.com/v2/account-verification/verified-payout/add-bank-accounts
    -X PUT \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>
    -d '<citizenTransactionId>'
curl https://testapi.paywithcitizen.com/v2/account-verification/verified-payout/add-bank-accounts
    -X PUT \
    -H Content-Type: application/json \
    -H AuthorizationCitizen: <your company public api key>
    -d '<citizenTransactionId>'

response: V2 create account verification session

  <Citizen Transaction ID>

cURL: V2 create account verification transaction

curl https://api.paywithcitizen.com/v2/account-verification/initialise-verified-out
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'
curl https://testapi.paywithcitizen.com/v2/account-verification/initialise-verified-out
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '<citizenTransactionId>'

response: V2 create account verification transaction

<Authorisation redirect URL>
V1 Endpoint V2 Endpoint
/v1/ais/initialise-verified-payout-no-email /v2/account-verification/verified-payout/add-bank-accounts
/v2/account-verification/initialise-verified-payout
V1 Request Field V2 Request Field
Session ID (Text Node) Citizen Transaction ID (String)































Create a payout transaction

If the user wishes to proceed with the payout then a permanent 'transaction' is created using your company public API key from your front end. Further authorisation from the user is not necessary for payouts, so this call returns a redirect which is set from your admin dashboard and usually points to your homepage. The call is made in both V1 and V2 of the API

cURL: V1 create payout transaction

curl https://api.paywithcitizen.com/v1/payouts/verified-payout
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '{
        "sessionId":<verified session ID>,
        "customerBankAccountId":<customer bank account ID>
      }'
curl https://testapi.paywithcitizen.com/v1/payouts/verified-payout
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key>
  -d '{
        "sessionId":<verified session ID>,
        "customerBankAccountId":<customer bank account ID>
      }'

response: V1 create payout transaction

  <redirect URL>

cURL: V2 create payout transaction

curl https://api.paywithcitizen.com/v2/payouts/verified/submit
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '{
        "citizenTransactionId":<Citizen transaction ID>,
        "customerBankAccountId":<customer bank account ID>
      }'
curl https://api.paywithcitizen.com/v2/payouts/verified/submit
  -X POST \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company public api key> \
  -d '{
        "citizenTransactionId":<Citizen transaction ID>,
        "customerBankAccountId":<customer bank account ID>
      }'

cURL: V2 create payout transaction

  <redirect URL>
V1 Endpoint V2 Endpoint
v1/payouts/verified-payout /v2/payouts/verified/transaction































Fetch the payout transaction

The transaction can be fetched at any stage after its creation. This example shows a transaction that has gone through the journey and accepted by the payout provider bank.

cURL: V1 fetch payout transaction

curl https://api.paywithcitizen.com/v1/payouts/transfer/<entityId>/<tokenId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>
curl https://testapi.paywithcitizen.com/v1/payouts/transfer/<entityId>/<tokenId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>

response: V1 fetch payout transaction

  {
    "id":<tokenId>,
    "tokenType":"PAYOUT",
    "tokenStatus":"GRANTED",
    "hashedUserEmail":"bc6cab45fbf15482be30e221d2be29e47e57a54505c12e25328b5d03531153db",
    "duration":0,
    "access":0,
    "decisionDate":1649683021818,
    "hashedRequesterEmail":"f081c929520016e50a472d2f0b9afda6e0034d65ec5e9128aec7a1623b7e4ba0",
    "creationDate":1649683021822,
    "needsManualReview":false,
    "payloadEncrypted":false,
    "merchantInternalId":"payments@merchant.com",
    "citizenPaymentId":"zSqEFpPBqaExStaPw7",
    "verifiedJourneySessionId":"f1ce6c59-9621-84b6-f35b-81e65b",
    "providerPaymentId":"298536",
    "counterPartyId":"62542a4da78d5c64d79bea8c",
    "accountInformationId":"62542a4ba78d5c64d79bea8b",
    "customerEmailAddress":"customer@gmail.com",
    "merchantEmailAddress":"payments@merchant.com",
    "currency":"GBP",
    "paymentGiro":"FPS",
    "merchantBankCode":"608384",
    "merchantAccountNumber":"40074844",
    "merchantBank":"CITIZEN_TEST_BANK",
    "counterPartyBank":"CITIZEN_TEST_BANK",
    "counterPartyBankCode":"040075",
    "counterPartyAccountNumber":"57583676",
    "firstName":"John",
    "lastName":"Doe",
    "amount":0.11,
    "reference":"TestPayout",
    "status":"ACCEPTED"
  }

cURL: V2 fetch payout transaction

curl https://api.paywithcitizen.com/v2/payouts/transaction/<citizenTransactionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>
curl https://testapi.paywithcitizen.com/v2/payouts/transaction/<citizenTransactionId>
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company private api key>

response: V2 fetch payout transaction

  {
    "citizenTransactionId":"2DgfcuGv4Fe3fhfv3J",
    "merchantId":"625536e7866f387953ca3e5a",
    "merchantTradingName":"Merchant Ltd",
    "customerIdentifier":"customer@gmail.com",
    "transactionStatus":"ACCEPTED",
    "creationDate":1649683021822,
    "testParameters":{},
    "citizenCounterPartyId":"625536ed866f387953ca3e5e",
    "providerPaymentId":"298585",
    "currency":"GBP",
    "paymentGiro":"FPS",
    "merchantBankCode":"608384",
    "merchantAccountNumber":"40074844",
    "merchantBank":"CITIZEN_TEST_BANK",
    "counterPartyBank":"CITIZEN_TEST_BANK",
    "counterPartyBankCode":"040075",
    "counterPartyAccountNumber":"57583676",
    "counterPartyAccountName":"John Doe",
    "amount":10.00,
    "reference":"TestPayment"
  }
V1 Endpoint V2 Endpoint
/v1/payouts/transfer/<entityId>/<tokenId> /v2/payouts/transaction/<citizenTransactionId>
V1 Request Field V2 Request Field
Token ID Citizen Transaction ID
Entity ID N/A
V1 Response Field V2 Response Field
id citizenTransactionId
hashedRequesterEmail merchantId
requesterUsername merchantTradingName
hashedUserEmail customerIdentifier
merchantInternalId customerIdentifier
tokenStatus transactionStatus

Corporate Account

Merchants can have a bank account hosted by one of Citizen's EMI partners. For such accounts, Corporate Account functionality allows viewing of account details and transactions. It is also possible to make withdrawals from such accounts from the merchant admin dashboard by first adding a counterparty with a signed request to the Citizen API. This functionality is available in V1 and V2 of the API.

Get corporate accounts

A list of corporate accounts can be fetched using your company private API key.

cURL: V1 fetch corporate accounts

curl https://api.paywithcitizen.com/v1/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>
curl https://testapi.paywithcitizen.com/v1/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>

response: V1 fetch corporate accounts

  [
    {
      "accountId": "<the id of the account>",
      "merchantDescription": "<friendly name of the account>",
      "customerNumber": "<the id of the owner>",
      "accountNumber": "<the account number or iban depending the giro>",
      "bankCode": "<sort code or bic depending the giro>",
      "giro": "<the giro of the account>",
      "currency": "<the currency of the account>",
      "balance": "<the available balance of the account>",
      "providerBankAccountId": "<the provide identifier of the account>",
      "accountHolderName": "<the account holder name of the account>"
    }
  ]

cURL: V2 fetch corporate accounts

curl https://api.paywithcitizen.com/v2/corporate-account/merchant/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>
curl https://testapi.paywithcitizen.com/v2/corporate-account/merchant/corporate-accounts \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>

response: V2 fetch corporate accounts

  [
    {
      "accountNumber":<account number or IBAN>,
      "bankCode":<Sort code or BIC>,
      "giro":<account giro>,
      "currency":<account currency>,
      "bank":<EMI partner bank>,
      "balance":<account balance>,
      "providerCustomerId":<customer ID on EMI partner bank>,
      "providerBankAccountId":<bank account ID on EMI partner bank>,
      "accountHolderName":<account holder name>
    }
  ]
V1 Endpoint V2 Endpoint
/v1/corporate-accounts /v2/corporate-account/merchant/corporate-accounts
V1 Response Field V2 Response Field
accountId N/A
customerNumber providerCustomerId
N/A bank




































Get corporate account transactions

Transactions for a corporate account can be fetched with your company private API key. This functionality is available in both V1 and V2 of the API.

cURL: V1 fetch corporate account transactions

curl https://api.paywithcitizen.com/v1/corporate-accounts/corporate-account-transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>
curl https://api.paywithcitizen.com/v1/corporate-accounts/corporate-account-transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>

response: V1 fetch corporate account transactions

{
  "content": [
    {
      "description": "<the description of the transaction>",
      "transactionType": "<the type of the transaction>",
      "clientReferenceNumber": "<the reference of the transaction>",
      "transactionAmount": "<amount in pennies or cents>",
      "transactionCurrency": "<transaction currency>",
      "counterPartyFirstName": "<counterparty first name>",
      "counterPartyLastName": "<counterparty last name>",
      "transactionDate": "<the creation date of the transaction>",
      "debit": "<shows if the transaction is debit or not>" 
    }
  ],
  "totalPages": "<total pages number>"
}

cURL: V2 fetch corporate account transactions

curl https://api.paywithcitizen.com/v2/corporate-account/transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>
curl https://testapi.paywithcitizen.com/v2/corporate-account/transactions/<providerBankAccountId> \
  -X GET \
  -H Content-Type: application/json \
  -H AuthorizationCitizen: <your company api key>

response: V2 fetch corporate account transactions

{
  "content": [
    {
      "reference": "<the reference of the transaction>",
      "transactionType":<transaction type>,
      "transactionId": "<the id of the transaction>",
      "transactionAmount":<amount in pennies or cents>,
      "transactionCurrency":<transaction currency>,
      "counterPartyFirstName":<counter party first name>,
      "counterPartyLastName":<counter party last name>,
      "transactionDate":<transaction date>,
      "debit":<flag indicating if the transaction is a debit>
    }
  ],
  "totalPages":1
}
V1 Endpoint V2 Endpoint
/v1/corporate-accounts/corporate-account-transactions/<providerBankAccountId> /v2/corporate-account/transactions/<providerBankAccountId>
V1 Response Field V2 Response Field
clientReferenceNumber transactionId
description reference














































Create a withdrawal counterparty

A counterparty must be added with a signed API request before a withdrawal can be made to that counterparty from your merchant admin dashboard. This is necessary for both V1 and V2 of the API.

cURL: V1 create withdrawal counterparty

curl https://api.paywithcitizen.com/v1/corporate-account/admin-counter-party \
  -X POST
  -H "AuthorizationCitizen: <private API key>" \
  -H "Signature: <request signature>" \
  -H "Expires-at: <signture expiry unix timestamp>" \
  -d '{
        "accountGiro": "FPS",
        "bankCountry": "GB",
        "accountCurrency": "GBP",
        "merchantInternalId": "Acquiring account",
        "firstName": "Internal",
        "lastName": "Account",
        "accountNumber": "12345678",
        "bankCode": "010203"
        "entityId": "6125e939e47af159a96932bc"
      }'
curl https://testapi.paywithcitizen.com/v1/corporate-account/admin-counter-party \
  -X POST
  -H "AuthorizationCitizen: <private API key>" \
  -H "Signature: <request signature>" \
  -H "Expires-at: <signture expiry unix timestamp>" \
  -d '{
        "accountGiro": "FPS",
        "bankCountry": "GB",
        "accountCurrency": "GBP",
        "merchantInternalId": "Acquiring account",
        "firstName": "Internal",
        "lastName": "Account",
        "accountNumber": "12345678",
        "bankCode": "010203"
        "entityId": "6125e939e47af159a96932bc"
      }'

response: V1 create withdrawal counterparty

{
  "entityId": <your entity ID>,
  "payoutProviderCustomerId": "228545",
  "counterPartyId": "6125e93ce47af159a96932bf",
  "merchantInternalId": "Acquiring account",
  "bankCountry": "GB",
  "accountCurrency": "GBP",
  "accountGiro": "FPS",
  "accountNumber": "12345678",
  "bankCode": "010203",
  "firstName": "Internal",
  "lastName":"Account"
}

cURL: V2 create withdrawal counterparty

curl https://api.paywithcitizen.com/v2/corporate-account/admin-counter-party \
  -X POST \
  -H "AuthorizationCitizen: <your private API key>" \
  -H "Signature: <request person>" \
  -H "Expires-at: <signture expiry unix timestamp>" \
  -d '{
        "payoutProviderCustomerId":"419001",
        "payoutProviderAccountId":"1244099",
        "customerIdentifier":"customer@gmail.com",
        "accountGiro":"FPS",
        "accountNumber":"57583676",
        "bankCode":"040075",
        "accountName":"John Doe"
      }'
curl https://testapi.paywithcitizen.com/v2/corporate-account/admin-counter-party \
  -H "AuthorizationCitizen: <your private API key>" \
  -H "Signature: <request person>" \
  -H "Expires-at: <signture expiry unix timestamp>" \
  -d '{
        "payoutProviderCustomerId":"419001",
        "payoutProviderAccountId":"1244099",
        "customerIdentifier":"customer@gmail.com",
        "accountGiro":"FPS",
        "accountNumber":"57583676",
        "bankCode":"040075",
        "accountName":"John Doe"
      }'

response: V2 create withdrawal counterparty

{
  "counterPartyId":"6267ee476b142342d3924bad",
  "payoutProviderCustomerId":"418955",
  "payoutProviderAccountId":"1244050",
  "customerIdentifier":"customer@gmail.com",
  "accountGiro":"FPS",
  "accountNumber":"57583676",
  "bankCode":"040075",
  "accountName":"John Doe"
}
V1 Endpoint V2 Endpoint
/v1/corporate-account/admin-counter-party /v2/corporate-account/admin-counter-party
V1 Response Field V2 Response Field
firstName accountName
lastName accountName
merchantInternalId customerIdentifier





















Web Hooks

Webhooks are broadly similar to V1 of the API, however there have been changes to field names, webhook type names and webhook transaction names.

Account Verification Webhook

V1 Account Verification Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"AISP_TOKEN_DECISION",
          "data":{
            "id":<token ID>,
            "version":"2",
            "journeyType":"NO_EMAIL",
            "tokenType":"BANK_ACCOUNT_INFORMATION",
            "tokenStatus":"GRANTED",
            "hashedUserEmail":<hashed customer email>,
            "encryptedUserEmail":<encrypted customer email>,
            "duration":0,
            "access":128,
            "decisionDate":1651066967272,
            "decisionHashedEmail":<hashed customer email>,
            "hashedRequesterEmail":<hashed merchant email>,
            "requesterUsername":"Merchant Ltd",
            "creationDate":1651066965147,
            "needsManualReview":false,
            "payloadEncrypted":false,
            "citizenTransactionId":<Citizen transaction ID>,
            "financialServiceProvider":"CITIZEN_TEST_BANK",
            "aisRequestStatus":"ACCEPTED",
            "accounts": [
              {
                "id":<customer bank account ID>,
                "accountName":"Main Account",
                "accountHolderName":<encrypted account holder name>,
                "number":<encrypted account number>,
                "partialAccountNumber":<last 3 digits of account number>
                "sortCode":<encrypted sort code>,
                "iban":<encrypted IBAN>,
                "partialIban":<last 3 digits of IBAN>,
                "bic":<encrypted BIC>,
                "type":"account",
                "details":<encrypted bank details>,
                "bank":"CITIZEN_TEST_BANK"
              }
            ]
          }
        }'

V2 Account Verification Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"ACCOUNT_VERIFICATION_DECISION",
          "data": {
            "citizenTransactionId":<Citizen transaction ID>,
            "merchantId":<your merchant ID>,
            "merchantTradingName":"Merchant Ltd",
            "customerIdentifier":<your identifier for the customer, eg email>
            "financialServiceProvider":"CITIZEN_TEST_BANK",
            "version":"2",
            "journeyType":"NO_EMAIL",
            "transactionStatus":"ACCEPTED",
            "creationDate": 1650534735,
            "testParameters":{},
            "accounts": [
              {
                "id":"fgF4H5GFcv64DvGKaqC",
                "accountName":<bank account name>,
                "accountHolderName":<account holder name>,
                "number":<account number>,
                "partialAccountNumber":<last 3 digits of account number>,
                "sortCode":<account sort code>,
                "iban":<account IBAN>,
                "partialIban":<last 3 digits of IBAN>,
                "bic":<account BIC>,
                "type":"account",
                "details":<bank account details>
                "bank":"CITIZEN_TEST_BANK"
              }
            ],
            "payloadEncrypted":false
          }
        }'
V1 Field V2 Field
id citizenTransactionId
tokenStatus transactionStatus
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
V1 Webhook Type V1 Transaction Status
AISP_TOKEN_CREATED REQUESTED
AISP_TOKEN_REDIRECT PENDING_AUTHORISATION
AISP_TOKEN_DECISION GRANTED or DECLINED
AISP_TOKEN_ERROR DECLINED
V2 Webhook Type V2 Transaction Status
ACCOUNT_VERIFICATION_CREATED INITIATED
ACCOUNT_VERIFICATION_REDIRECT PENDING_AUTHORISATION
ACCOUNT_VERIFICATION_DECISION ACCEPTED or DECLINED
ACCOUNT_VERIFICATION_ERROR DECLINED



















































Pay-in Webhook

V2 Pay-in Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"PAYMENT_DECISION",
          "data": {
            "id":<token ID>,
            "version":"2",
            "journeyType":"VERIFIED_DEPOSIT_NO_EMAIL",
            "tokenType":"PAYMENT",
            "tokenStatus":"GRANTED",
            "hashedUserEmail":<hashed customer email>,
            "duration":0,
            "access":128,
            "hashedRequesterEmail":<hashed merchant email>,
            "requesterUsername":<your merchant name>,
            "creationDate":1651067713379,
            "creationIpAddress":"192.168.1.2",
            "needsManualReview":false,
            "payloadEncrypted":false,
            "merchantInternalId":<your ID for the customer, eg email address>,
            "paymentProvider":"CITIZEN_TEST_BANK",
            "paymentMethod":"OTHER",
            "paymentGiro":"FPS",
            "citizenPaymentId":<Citizen payment ID>,
            "paymentStatus":"ACCEPTED",
            "paymentAmount":1.02,
            "paymentCurrency":"GBP",
            "shortReference":<payment short reference>,
            "detailedReference":<payment detailed reference>,
            "accountInformationId":<AIS token ID>
          }
        }'

V2 Pay-in Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"PAYIN_DECISION",
          "data": {
            "citizenTransactionId":<Citizen transaction ID>,
            "merchantId":<your merchant ID>,
            "merchantTradingName":<your company name>,
            "customerIdentifier":<your ID for the customer, eg email address>,
            "version":"2",
            "journeyType":"NO_EMAIL",
            "transactionStatus":"ACCEPTED",
            "creationDate":1650534735,
            "testParameters":{},
            "paymentMethod":"OTHER",
            "paymentGiro":"FPS",
            "merchantAccountNumber":<your pay-in account number or IBAN>,
            "merchantBankCode":<your pay-in sort code or BIC>,
            "paymentAmount":<payment amount>,
            "paymentCurrency":"GBP",
            "reference":<payment reference>,
            "paymentProvider":"CITIZEN_TEST_BANK",
            "payloadEncrypted":false
          }
        }'
V1 Field V2 Field
id citizenTransactionId
tokenStatus transactionStatus
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
citizenPaymentId citizenTransactionId
V1 Webhook Type V1 Transaction Status
PAYMENT_CREATED REQUESTED
PAYMENT_REDIRECT PENDING_AUTHORISATION
PAYMENT_DECISION GRANTED or DECLINED
PAYMENT_ERROR DECLINED
V2 Webhook Type V2 Transaction Status
PAYIN_CREATED INITIATED
PAYIN_REDIRECT PENDING_USER_AUTHORISATION
PAYIN_DECISION ACCEPTED or DECLINED
PAYIN_ERROR DECLINED































Payout Webhook

V1 Payout Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"PAYOUT_ACCEPTED",
          "data": {
            "id":<token ID>,
            "tokenType":"PAYOUT",
            "tokenStatus":"GRANTED",
            "hashedUserEmail":<hashed customer email>,
            "duration":0,
            "access":0,
            "decisionDate":1651068926887,
            "hashedRequesterEmail":<hashed merchant email>,
            "creationDate":1651068926891,
            "needsManualReview":false,
            "payloadEncrypted":false,
            "citizenPaymentId":<Citizen payment ID>,
            "verifiedJourneySessionId":<verified journey session ID>,
            "providerPaymentId":<EMI partner payment ID>,
            "counterPartyId":<Citizen counter party ID>,
            "accountInformationId":<AIS token ID>,
            "currency":"GBP",
            "paymentGiro":"FPS",
            "merchantBank":"CITIZEN_TEST_BANK",
            "counterPartyBank":"CITIZEN_TEST_BANK",
            "amount":0.11,
            "reference":<payment reference>,
            "status":"ACCEPTED"
          }
        }'

V2 Payout Webhook

  curl https://<your backend server> \
    -X POST \
    -H "Content-Type:application/json" \
    -H "Citizen-Signature: <request signature signed with Citizen key>" \
    -H "Citizen-Public-Api-Key: <your public API key> \
    -d '{
          "type":"PAYOUT_ACCEPTED",
          "data": {
            "citizenTransactionId":<Citizen transaction ID>,
            "merchantId":<your merchant ID>,
            "merchantTradingName":<your company trading name>,
            "customerIdentifier":<your ID for the customer, eg email address>,
            "transactionStatus":"ACCEPTED",
            "creationDate":1650534735,
            "testParameters":{},
            "citizenCounterPartyId":<Citizen counter party ID>,
            "providerPaymentId":<EMI partner payment ID>,
            "currency":"GBP",
            "paymentGiro":"FPS",
            "merchantBank":"CITIZEN_TEST_BANK",
            "counterPartyBank":"CITIZEN_TEST_BANK",
            "counterPartyAccountName":"customer bank account name",
            "amount":<payment amount>,
            "reference":"Test payment",
            "payloadEncrypted":false
          }
        }'
V1 Field V2 Field
id citizenTransactionId
verifiedJourneySessionId citizenTransactionId
tokenStatus transactionStatus
hashedUserEmail customerIdentifier
hashedRequesterEmail merchantId
citizenPaymentId citizenTransactionId
V1 Webhook Type V1 Transaction Status
PAYOUT_CREATED REQUESTED
PAYOUT_ACCEPTED GRANTED
PAYOUT_ERROR DECLINED
PAYOUT_REJECTED DECLINED
V2 Webhook Type V2 Transaction Status
PAYOUT_CREATED ACCEPTED
PAYOUT_ACCEPTED ACCEPTED or DECLINED
PAYOUT_ERROR DECLINED

JavaScript SDK

The JavaScript SDK is broadly similar between V1 and V2. The SDK major version has been changed from V4 to V5.

Account Verification SDK

The account verification SDK is first included on your site. The path has been updated for V2.

V1 Account Verification SDK include

<script src="https://sdk.citizen.is/v4/sdk/sdk-account-information.js"></script>

V2 Account verification SDK include

<script src="https://sdk.paywithcitizen.com/v5/sdk/sdk-account-verification.js"></script>











The SDK must be initialised with your public API key before use.

V1 Account Verification SDK initialisation

<script>
window.citizenAsyncInit = function () {
    CITIZEN_ACCOUNT_INFORMATION.init({
      publicApiKey: '[Your-entity-public-api-key]'
    })
  };
</script>

V2 Account Verification SDK initialisation

window.citizenAsyncInit = function () {
    CITIZEN_ACCOUNT_VERIFICATION.init({
      publicApiKey: '[Your-entity-public-api-key]'
    })
  };
</script>





















The account verification journey is initialised with the Citizen transaction ID from your back end, and optionally the customer email address and a callback.

V1 Account Verification journey initialisation

<script>
let accountRequest = function (transactionId, options) {
    window.CITIZEN_ACCOUNT_INFORMATION.startAccountInformationRetrievalJourney(transactionId, options);
  }
</script>

V2 Account Verification journey initialisation

<script>
let accountRequest = function (transactionId, options) {
    window.CITIZEN_ACCOUNT_VERIFICATION.startAccountVerificationJourney(transactionId, options);
  }
</script>





















Pay-in SDK

The pay-in SDK is first included on your site. The path has been updated for V2.

V1 Pay-in SDK include

<script src="https://sdk.citizen.is/v4/sdk/sdk-payment.js"></script>

V2 Pay-in SDK include

<script src="https://sdk.paywithcitizen.com/v5/sdk/sdk-payin.js"></script>











The SDK must be initialised with your public API key before use.

V1 Pay-in SDK initialisation

<script>
  window.citizenAsyncInit = function () {
  CITIZEN_PAYMENT.init({
    publicApiKey: '[Your-entity-public-api-key]'
    })
  };
</script>

V2 Pay-in SDK initialisation

<script>
  window.citizenAsyncInit = function () {
  CITIZEN_PAYIN.init({
    publicApiKey: '[Your-merchant-public-api-key]'
    })
  };
</script>





















The pay-in journey is initialised with the Citizen transaction ID from your back end, and optionally the customer email address and a callback.

V1 Pay-in journey initialisation

<script>
  let sendPayment = function (transactionId, options) {
    window.CITIZEN_PAYMENT.startPaymentJourney(transactionId, options);
  }
</script>

V2 Pay-in journey initialisation

<script>
  let sendPayIn = function (citizenTransactionId, options) {
    window.CITIZEN_PAYIN.startPayInJourney(citizenTransactionId, options);
  }

</script>





















Payout SDK

The payout SDK is first included on your site. The path has been updated for V2.

V1 payout SDK include

<script src="https://sdk.citizen.is/v4/sdk/sdk-payout.js"></script>

V2 payout SDK include

<script src="https://sdk.paywithcitizen.com/v5/sdk/sdk-payout.js"></script>
















The SDK must be initialised with your public API key before use.

V1 payout SDK initialisation

<script>
  window.citizenAsyncInit = function () {
  CITIZEN_PAYOUT.init({
    publicApiKey: '[Your-entity-public-api-key]'
    })
  };
</script>

V2 payout SDK initialisation

<script>
  window.citizenAsyncInit = function () {
  CITIZEN_PAYOUT.init({
    publicApiKey: '[Your-merchant-public-api-key]'
    })
  };
</script>





















The payout journey is initialised with the Citizen transaction ID from your back end, and optionally the customer email address and a callback.

V1 Account Verification journey initialisation

<script>
  let verifiedPayoutRequest = function (transactionId, options) {
    window.CITIZEN_PAYOUT.startVerifiedPayoutJourney(transactionId, options);
  }

</script>

V1 Account Verification journey initialisation

<script>
  let verifiedPayoutRequest = function (transactionId, options) {
    window.CITIZEN_PAYOUT.startVerifiedPayoutJourney(transactionId, options);
  }

</script>

Errors

The Citizen API uses the following error codes:

Error Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key or Client Secret is wrong.
403 Forbidden -- Your account is restricted to perform that operation
404 Not Found -- The specified object could not be found.
405 Method Not Allowed -- You tried to access the API with an invalid method.
406 Not Acceptable -- You posted incorrect JSON
409 Conflict -- That object already exists (commonly the email of the account)
500 Internal Server Error -- Server side issues that are not caught by the above. Try again later.
503 Service Unavailable -- Citize is offline for maintenance. Please try again later.