The Atlar API is a resource-based JSON API, following RESTful standards.

What is considered a Non-Breaking change

Endpoints, Fields and Request parameters will not be removed or changed without a change of version. However non-breaking changes to the API may be introduced without a version change. This includes new fields to JSON objects and new parameters to requests. In practice, this means that your clients need to handle unknown/unmapped fields and parameters. Introduction of new fields/parameters will not impact behavior of existing fields/parameters.

Lastly, there may exist fields that are not documented in this API reference. These fields are not considered official and above protection does not apply. Atlar may change those at any point, so usage thereof is at own risk.


The Atlar API is resource-oriented and follows REST/CRUD practices.

Method & PathDescription
GET /resourceQuery among all of the same resource
POST /resourceCreate a new resource
GET /resource/{id}Get the identified resource
PUT /resource/{id}Update the identified resource (might be removed in favor for PATCH)
PATCH /resource/{id}Update specific fields of the identified resource (not implemented yet)
DELETE /resource/{id}Delete the identified resource


No trailing slashes, and all paths have pluralized resource names


Use Basic auth with the <ACCESS_KEY> and <SECRET> that you retrieve when creating programmatic access tokens.

Listing Accounts using curl would be done with:

curl 'https://api.atlar.com/v1/accounts' -u '<ACCESS_KEY>:<SECRET>'


Pagination works the same on all Query endpoints. This includes GET endpoints of resource without specified identifier (e.g. GET /transactions or GET /payments).

The request has two common query parameters that can be used: limit and token. Limit, if included, must be between 1 and 500, if it is less or more than those values it will be coerced down or up to the respective limit. If limit is not included it will default to 100.


  • GET /transactions?limit=20 will give 20 transactions (or up to 20 if there is less than 20 total transactions)

  • GET /transactions?limit=20&token={TOKEN} will give 20 transactions, starting from {TOKEN}.

The response object will, no matter the resource, have the same shape. It will carry the four fields token and limit (indicating what limit and token that were used for the request), as well as nextToken and items. The string nextToken, if non-empty, indicates what token one should use to continue pagination. It is empty if it is known to not exist any more items on the server at the point of the request. The list of items carries the queried resources.

Never use the length of the items list in comparison to the limit to determine whether more pages exists. There may be (uncommon) cases where the number of items returned, is lower than the limit that was asked for, even though more items exists on the server.


If you are using resource-specific filtering parameters when querying, those should stay the same throughout the pagination. If query parameters change, always restart pagination (by not setting token value in the request)


  "nextToken": "the token that should be used to get the next page, empty if no more",
  "token": "the token used for this query",
  "limit": 20,
  "items": []


The Atlar API has built-in support for idempotency. Idempotency makes it possible to safely retry requests without risking to perform the same intended operation twice. This is in particular useful, or even needed, when an API request is disrupted, or in some way failing in transit, which renders the client without a response from the server. The problem in the scenario is that the client that didn't receive the response cannot know if the server actually performed the request or not. Which, without Idempotency, for a creation of a resource could lead to the multiple created resources if the request is retried. To solve this, Idempotency Keys are implemented and, if supplied, the same request can be retried multiple times, without the risk of multiple resources being created.

To make, an otherwise non-idempotent request, idempotent; supply the optional HTTP header Idempotency-Key. The key is ignored for GET and DELETE requests and will expire automatically after 24h of first use. The uniqueness of a key is bound to the organization and endpoint (method and path). If a client would make a second request - with the same idempotency key - before the first request is done server-side, the server will respond with a 425 Too Early status. This error code is always safe to retry.

If one wants to test how idempotency works on the Atlar API, an endpoint with the sole purpose of testing has been created on: POST /v1/idempotency-test. It does not take any request body, but one can supply the two optional integer query-parameters status and sleep. The former will dictate which status the response will have, and the latter will make the server sleep for this amount of milliseconds. The server will, after any potential sleep, respond with the supplied idempotency key, a random uuid, as well as the requested status.

With this endpoint one can test how the API will behave when supplying an idempotency key.

Webhooks and events

Webhooks allow you to receive HTTP POST payloads whenever certain events happen. You can use the Webhook endpoints to add new or modify existing webhook configurations for your organization. The endpoints allow you to specify at which HTTPS URL you want to receive your webhook calls.


Webhooks are based on our data model called events which exist on multiple resources in the API. For example, the payment events endpoint lets you see everything that has happened to a given payment. Events for different resources (payments, direct_debits, etc.) share the same general structure. The table below shows which events that are possible for each resource.

API resourceEvents

Note1: when creating a webhook you must provide an inclusive filter to specify which events you want to receive. Note2: the event DELETED, might not be an event that can occur in a regular business process. However, in some cases, maybe because of an issue on either Bank or Atlar side, there may be need for data cleanup, and in that case DELETED event will be sent.

Webhook payload

The webhook payload contains:

  • Information about the event that occurred (event and resource).
  • The underlying resource that was affected by the event (entity).

Below is an example of a direct debit that was returned by the bank due to a refund.

    "resource": "direct_debits",
    "event": {
        "organizationId": "208086fb-f8c3-402c-bb6d-820aa57f782d",
        "entityId": "1d0fe618-3f76-407a-9635-fbebc3783256",
        "id": 5,
        "timestamp": "2022-12-13T14:00:00Z",
        "name": "RETURNED",
        "originator": "AVANTIR",
        "message": "RefundRequestByEndCustomer — Return of funds requested by end customer",
        "details": {
            "returnReason": "MD06",
            "transactionId": "e006e543-e60a-449e-9444-f343e02d6343"
    "entity": "{...}"

Webhook retries

Webhook requests are retried should they fail or if your server responds with anything but 2xx HTTP statuses.

These systematic retries will happen 3 times every 15 minutes during 12 hours. If your system is down for more than 12 hours, or you lost your webhook information in some other way, you can retrieve the lost information on the event endpoints on the API to get all historic events for a specific resource.

Webhook security

We will send a few HTTP headers with each webhook request. Below is a summary of what headers we send and why.

  • Signature
    • Header: Webhook-Signature (case-insensitive)
    • Format: HMAC-SHA256
    • Example: e71354d023f850abcb1bfd884de2874bc973dcbb4b44acac5fe4ca89e28d12ed (multiple signatures will be separated by a comma ,)
    • Purpose: Makes it possible for your app to verify that the webhook was sent from Atlar (authenticity), and that the message hasn't been altered (integrity).
  • Timestamp
    • Header: Webhook-Request-Timestamp (case-insensitive)
    • Format: RFC3339 (UTC, nano)
    • Example: 2022-10-11T10:13:14.000000015Z
    • Purpose: Used to mitigate replay attacks. The value is set to the time that the HTTP request was made.

Webhook signature and timestamp verification

We will generate a unique webhook key for each and one of your endpoints, which you will see once after creating it via the Webhook API. Store the key as a secret in your application. We use this key to create the HMAC-SHA256 signature, and you can use it to validate our signature.

In your application, write a function that executes the following steps:

  1. Decode the key into bytes. The key is a byte array with standard base64 encoding.
  2. Create the signature payload by concatenating:
    1. The webhook request body as a JSON string.
    2. A period ..
    3. The timestamp from the header (as a string).
  3. Compute a HMAC (Hash-based Message Authentication Code) using the SHA256 hash function.
  4. Encode the HMAC bytes into a hexadecimal string so it can be compared with the value in the signature header.
  5. Compare the two values using a constant-time string comparison (not a plain ==). This helps to mitigate timing attacks. The HMAC library you're using might have an equality function for this purpose.
  6. Compare the time difference between the request timestamp and the current time (UTC) and check that the message isn't too old. You can choose a certain time tolerance such as 5min. This helps to prevent replay attacks, where an attacker would re-send an old request.

Note that if you have multiple keys for one endpoint (during key rotation), there will be multiple signatures separated by a comma ,.

Code example (Go)
func signatureIsValid(sigHeader, tsHeader, payload, base64Key string) (bool, error) {
	key, err := base64.StdEncoding.DecodeString(base64Key)
	if err != nil {
		return false, fmt.Errorf("failed to decode key, %w", err)

	sig, err := hex.DecodeString(sigHeader)
	if err != nil {
		return false, nil

	mac := hmac.New(sha256.New, key)
	mac.Write([]byte(payload + "." + tsHeader))

	return hmac.Equal(mac.Sum(nil), sig), nil
Example values
Webhook-Signature: 224abe2da571af8a46ed70b52de1cbb4b5142f55056500e2bfddfc1968cf4ae5
Webhook-Request-Timestamp: 2022-10-06T07:26:57.237369365Z
Key (base64): agj+xWKk3gqkP+SsCsljkjbDth7bxguqVMRd4K3wm1I=
Body: {"resource":"payments","event":{"organizationId":"1f91e001-9295-46b6-9438-ef6f0fed18fc","entityId":"422a164c-4548-11ed-8d31-0a58a9feac02","id":0,"timestamp":"2022-10-06T07:26:56.837022728Z","name":"CREATED","originator":"90b51164-d782-4238-a0bf-c1ff66a8a83e","message":"","details":{"request":"ewogICAgImFtb3VudCI6IHsKICAgICAgICAiY3VycmVuY3kiOiAiU0VLIiwKICAgICAgICAidmFsdWUiOiA1MDAwCiAgICB9LAogICAgInNvdXJjZUFjY291bnRJZCI6ICJjZjAxMGY5MC04M2UyLTQyMGQtYjE2ZS1lYjY4ZGJmZDcwNDciLAogICAgImRlc3RpbmF0aW9uRXh0ZXJuYWxBY2NvdW50SWQiOiAiZTgwZGU1ZTAtMWIwNS00OTc0LThjNzMtMDg0MDI3MTg1YzdmIiwKICAgICJkYXRlIjogIjIwMjItMTAtMTAiLAogICAgInJlbWl0dGFuY2VJbmZvcm1hdGlvbiI6IHsKICAgICAgICAidHlwZSI6ICJVTlNUUlVDVFVSRUQiLAogICAgICAgICJ2YWx1ZSI6ICJUZXN0aW5nIHdlYmhvb2tzIgogICAgfSwKICAgICJwYXltZW50U2NoZW1lVHlwZSI6ICJTQ1QiCn0="}},"entity":{"amount":{"currency":"SEK","value":5000},"approvalChain":{"approvalSteps":[{"id":"b698ab4e-efec-4c97-9133-88904022cd1b","requiredRole":{"id":"4ce82d1a-0ebf-46e7-96ba-7664cd48e4af","name":"Owner","owner":true},"status":"PENDING","updated":"0001-01-01T00:00:00Z"}],"triggeredApprovalChainId":"54e2c37d-fa95-4d5b-bbcf-b99c8ff8676f"},"attachedTransactions":[],"created":"2022-10-06T07:26:56.836974Z","date":"2022-10-10","destinationCounterparty":{"id":"7af0ea0b-7366-42aa-ad83-49e93e41ce5d","identifiers":[],"name":"Company Inc.","partyType":""},"destinationExternalAccount":{"bank":{"bic":"12345678900","id":"","name":""},"counterpartyId":"7af0ea0b-7366-42aa-ad83-49e93e41ce5d","id":"e80de5e0-1b05-4974-8c73-084027185c7f","identifiers":[{"holderName":"Test","market":"DE","number":"DE66500105172794778236","type":"IBAN"}],"organizationId":"1f91e001-9295-46b6-9438-ef6f0fed18fc"},"externalMetadata":null,"id":"422a164c-4548-11ed-8d31-0a58a9feac02","organizationId":"1f91e001-9295-46b6-9438-ef6f0fed18fc","paymentScheme":{"displayName":"Sepa Credit Transfer","type":"SCT"},"paymentSchemeType":"SCT","reconciliation":{"status":""},"remittanceInformation":{"type":"UNSTRUCTURED","value":"Testing webhooks"},"sourceAccount":{"affiliation":{"id":"49b72264-fae5-4720-89cf-f0d6fd5f727e","name":"My bank"},"bank":{"bic":"ATLRSESSXXX","id":"3dde93bf-49d1-44e4-a288-4bf0d4e31574","name":"Testbank"},"id":"cf010f90-83e2-420d-b16e-eb68dbfd7047","identifiers":[{"holderName":"Test Testsson","market":"DE","number":"DE77500105179251553356","type":"IBAN"}],"name":"My EUR account","owner":{"name":""}},"status":"CREATED"}}

Webhook idempotency and ordering of events

It's important that your webhook handler is idempotent. This means that two identical webhook calls will give the same result as one single call. Due to our behavior with retries, we can't guarantee that we won't send a webhook twice. Additionally, an attacker could resend a webhook in a replay attack. One way of achieving idempotency is to rely on a combination of the event.id (example: 3) and the entity.id (example: e4588d47-8d7e-48a0-a6f5-6960a19b45b3). These IDs will stay the same between retries, and combined they are unique for the given event that occurred. You can extract the IDs from the webhook payload and check if you have processed it before by storing previously seen IDs somewhere. Atlar will never send webhooks that are older than 120 hours, meaning you won't have to store the IDs for longer than that.

In addition to idempotency, it's important that your handler isn't strictly dependent on the ordering of the webhook calls. In most cases, webhooks will naturally be delivered in order, but this is no guarantee. To know the end state of the entity for the webhook, you can make use of the event.timestamp and entity fields. The entity always contains the entity as it looked like right after an event occurred. For example, if an event occurred that resulted in a status change of a payment, the entity will contain the updated payment with the new status.

Webhook key rotation

Webhook keys can be rotated as many times as you want. If your webhook key is compromised, you can use the rotation functionality to generate a new one. Use the /v1/webhooks/{id}/keys endpoints to generate or delete keys. Since you can have between 1 and 2 keys at once, rotation can happen without any application downtime.

Going live with webhooks

Before you can start receiving webhook calls from Atlar, the webhook configuration must be verified. You can contact us at [email protected] for this purpose. Note that modifying the url of a webhook will set verified to false.


In general the Atlar API uses the following standard error codes

400 Bad RequestThe request is invalid
401 UnauthorizedThe authentication is wrong or non-existent
403 ForbiddenThe used API key does not have permission to view/modify the resource
404  Not FoundThe identified resource could not be found
405  Method Not AllowedThe accessed method is not valid
410  GoneThe identified resource does not exist anymore
425Too EarlyIf same idempotency key is used for multiple requests, but the server is not done with previous request. Client is supposed to retry this status. See more about Idempotency above.
429  Too Many RequestsThe amount of requests made for the past minute exceeds our rate limit
500  Internal Server ErrorSome unexpected server-side issue has occurred
503  Service UnavailableTemporarily offline for maintenance

Request IDs

Each API request will be assigned a unique request identifier. This identifier will be included in the API response in the HTTP header request-id. If you need to contact us about a particular request, please provide the request identifier.