Sign your payments requests

Generate public and private keys and use them to sign API requests.

The Payments API v3 uses a pair of ECDSA keys in addition to a bearer token. The keys are a form of encryption that ensures your payments are secure.

With this form of encryption, you upload one public key to your Console account, and provide the other private key with your Payments v3 API requests. The combination of keys verifies and authenticates the request.

Payments API signing libraries

Client libraries

We provide client libraries and signing libraries in a variety of programming languages to simplify the integration process.

To make this process easier, we recommend using one of the client libraries, which include functionality for signing requests and webhooks. Request signing and validating webhook signatures are the most complex and time-consuming parts of integration for the Payments API. Client libraries help simplify this so you can get up and running faster.

If you're not using the client libraries, you can also sign requests manually, but we don't recommend using this method.

Signing libraries

Alternatively, if you want to use your own code, you can also implement one of our signing libraries just for this purpose.

To explore our client libraries and signing libraries, visit the TrueLayer Github. There are also example requests in the procedure below.

We offer signing libraries in these languages:

1. Sign up for Console

Before you can sign your requests, you must sign up for a Console account.

2. Generate your key pair

To generate your key pair, you can use OpenSSL or LibreSSL, which are installed on MacOS and Windows by default. To do so:

  1. Open the command line.
  2. Use these commands to generate your key pair:
openssl ecparam -genkey -name secp521r1 -noout -out ec512-private-key.pem
openssl ec -in ec512-private-key.pem -pubout -out ec512-public-key.pem

The keys, named ec512-private-key.pem and ec512-public-key.pem by default, are generated in your current working directory.

Ensure that you store your private key securely.

To learn more about how to use the command line, see the Apple or Microsoft documentation.

3. Upload your public key to Console

  1. Go to Console > Payments > Settings.
  2. Upload your public key in the Signing keys box.
    When you upload your public key, a KID is generated on this page. This is different to your public key itself, and is required to sign your requests.

4. Use a library to set up request signing

The end result of setting up request signing is that you can include a correctly formatted header named Tl-signature with your requests. This header includes the private key you generated earlier.

We recommend using our backend libraries. These include functionality to sign requests and webhooks for you, without having to sign them manually or use a separate signing library. In our Github, we maintain request signing libraries in a range of languages, so you can use the one best suited to your integration.

For example, a Tl-Signature looks like this in our Java request signing library:

// `Tl-Signature` value to send with the request
String tlSignature = Signer.from(kid, privateKey)
        .header("Idempotency-Key", idempotencyKey)
        .method("post")
        .path(path)
        .body(body)
        .sign();
// 
using TrueLayer.Signing;

// `Tl-Signature` value to send with the request.
var tlSignature = Signer.SignWithPem(kid, privateKey)
    .Method("POST")
    .Path(path)
    .Header("Idempotency-Key", idempotency_key)
    .Body(body)
    .Sign();
// require using Composer
$ composer require truelayer/signing

// create a Signer instance
use TrueLayer\Signing\Signer;
$signer = Signer::signWithPemFile('kid-value', '/path/to/privatekey');

// create a signature
use TrueLayer\Signing\Signer;

$signature = $signer->method('POST')
    ->path('/path') // The api path
    ->header('Idempotency-Key', 'my-key') // The idempotency key you must send with your request
    ->body('stringified request body')
    ->sign();    
# install signing library
pip install truelayer-signing

# `Tl-Signature` header value to send with the request
tl_signature = sign_with_pem(KID, PRIVATE_KEY) \
    .set_method(HttpMethod.POST) \
    .set_path(path) \
    .add_header("Idempotency-Key", idempotency_key) \
    .set_body(body) \
    .sign()
# install the gem manually
gem install truelayer-signing

# set your public and private key as variables
require "truelayer-signing"

TrueLayerSigning.certificate_id = "your-kid"
TrueLayerSigning.private_key = "your-private-key"

# `Tl-Signature` header value to send with the request
tl_signature = TrueLayerSigning.sign_with_pem
  .set_method("POST")
  .set_path(path)
  .add_header("Idempotency-Key", idempotency_key)
  .set_body(body)
  .sign
// `Tl-Signature` value to send with the request.
let tl_signature = truelayer_signing::sign_with_pem(kid, private_key)
    .method(Method::Post)
    .path("/payouts")
    .header("Idempotency-Key", idempotency_key)
    .body(body)
    .build_signer()
    .sign()?;
// `Tl-Signature` value to send with the request.
signature, err := tlsigning.SignWithPem(Kid, privateKeyBytes).
        Method("POST").
        Path("/payouts").
        Header("Idempotency-Key", idempotencyKey).
        Body(body).
        Sign()

The path variable refers to everything that comes after truelayer.com in the API request.

Common request signing issues

When you set up request signing, make sure to check for these common issues.

  • Whichever library you use, ensure that the path that you sign is the same as the path used to send the request. For example, if you're testing https://api.truelayer-sandbox.com/test-signature, the path must be /test-signature.
  • All signed headers sent with a request must be exactly as they were signed, with none missing. So if you sign Idempotency-Key you must also send exactly the same value in the request.
  • Ensure that the body passed to the signing library matches the body sent with the request exactly, byte for byte. It must not be formatted differently or have fields in a different order.
  • Ensure the request body has no trailing newlines if it was not signed that way.
  • Ensure the public key and private key you are signing with match the KID (Public Key ID) in our Console. You can find your KID by going to the Settings tab, then scrolling down to Payments settings.
**Payments settings** inside the **Settings** tab, with example sandbox public key.

Payments settings inside the Settings tab, with example sandbox public key.

5. Test your request signing

You can send a POST or DELETE request to our /test-signature endpoint to check if your configuration works. If correctly configured, the endpoint validates your Tl-signature and returns a 204 No Content response. It doesn't validate your request body.

This is an example of a request to the /test-signature endpoint:

curl -X POST \
	-H "Authorization: Bearer ${access_token}" \
	-H "Tl-Signature: ${signature}" \
	--data '{"nonce":"9f952b2e-1675-4be8-bb39-6f4343803c2f"}' \
	https://api.truelayer-sandbox.com/test-signature