Links

Testing API

At Swan, we are eager to provide a Sandbox that's as close as possible to real life. Our simulation API lets you trigger events happening outside of Swan, and our system reacts as it would in real life, displaying the right information at the right time.
The β€œdevelopers” section in the Dashboard provides some tools to help you perform tests easily by creating sandbox users and simulating external events that aren't generated by our APIs:
  • Sandbox users
    • Partner creates multiple pretend users and selects the one they want to impersonate in the Sandbox
  • Event simulator
    • Provides a list of external events which can be simulated.
If you want to manually test a scenario, or mock a behavior before implementing it, you'll use those 2 tools. To improve your integration experience with Swan, you can also simulate operations with the API; you can call them programmatically and perform end-to-end tests without connecting to the Dashboard.
This separate GraphQL API is called "Testing API", and it's available only in Sandbox. This API has two types of mutations and queries:
  • Sandbox User: these queries and mutations allow you to create a new sandbox user, update it, and endorse their identity
  • Event Simulator: these mutations allow you to test external events by simulating SEPA Transfers coming to the swan account, card payments, etc.
You can always call this API using a User Access Token (Your user in the dashboard), and sometimes using a Project Access Token.
This API has a different endpoint.

Sandbox User API

Create Sandbox User

To create a Sandbox user, call createSandboxUser.
This mutation must be called with a User Access Token from the Sandbox environment.
Request
Input
Response
mutation Mutation($input: CreateSandboxUserInput!) {
createSandboxUser(input: $input) {
... on CreateSandboxUserSuccessPayload {
sandboxUser {
id
mobilePhoneNumber
lastName
firstName
birthDate
nationalityCCA3
idVerified
autoConsent
preferredNotificationChannel
verificationStatus
identificationStatus
isActive
}
}
... on NationalityNotFoundRejection {
message
identifier
__typename
}
... on ForbiddenRejection {
message
__typename
}
}
}
{
"input": {
"firstName": "Partner",
"lastName": "Admin",
"birthDate": "1999-01-01",
"nationalityCCA3": "FRA",
"verificationStatus": "Verified",
"identificationStatus": "ValidIdentity",
"autoConsent": false
}
}
{
"data": {
"createSandboxUser": {
"sandboxUser": {
"id": "{{YOUR_SANBOX_USER_ID}}",
"mobilePhoneNumber": null,
"lastName": "Admin",
"firstName": "Partner",
"birthDate": "1999-01-01",
"nationalityCCA3": "FRA",
"idVerified": true,
"autoConsent": false,
"preferredNotificationChannel": null,
"verificationStatus": "Verified",
"identificationStatus": "ValidIdentity",
"isActive": false
}
}
}
}
You can use this mutation when you want to create a new user, for example, just before finalizing an onboarding, or when adding a new account membership.

Update a sandbox user

To update a Sandbox User, call updateSandboxUser.
This mutation must be called with a User Access Token from the Sandbox environment.
Request
Input
Response
mutation UpdateSandboxUser($input: UpdateSandboxUserInput!) {
updateSandboxUser(input: $input) {
... on UpdateSandboxUserSuccessPayload {
sandboxUser {
id
mobilePhoneNumber
lastName
firstName
birthDate
idVerified
nationalityCCA3
autoConsent
preferredNotificationChannel
verificationStatus
identificationStatus
isActive
}
}
... on ForbiddenRejection {
message
__typename
}
... on NationalityNotFoundRejection {
message
identifier
__typename
}
... on SandboxUserNotFoundRejection {
message
userId
__typename
}
}
}
{
"input": {
"id": "{{YOUR_ID}}",
"firstName": "Partner updated",
"lastName": "Admin updated",
"birthDate": "1999-01-02",
"nationalityCCA3": "MAR",
"verificationStatus": "NotStarted",
"identificationStatus": "Uninitiated",
"autoConsent": true
}
}
{
"data": {
"updateSandboxUser": {
"sandboxUser": {
"id": "{{YOUR_SANBOX_USER_ID}}",
"mobilePhoneNumber": null,
"lastName": "Admin updated",
"firstName": "Partner updated",
"birthDate": "1999-01-02",
"idVerified": false,
"nationalityCCA3": "MAR",
"autoConsent": true,
"preferredNotificationChannel": null,
"verificationStatus": "NotStarted",
"identificationStatus": "Uninitiated",
"isActive": false
}
}
}
}
Use this mutation when you want to update an existing user. This can happen when they verify their identity and the information they provided was doesn't match what we have on record (extracted from their ID document). It's also used to simulate updates of the identificationStatus.

Endorse a sandbox user

To switch the sandbox user you are logged in as, call endorseSandboxUser
This mutation must be called with a User Access Token from the Sandbox environment.
Request
Input
Response
mutation Mutation($input: EndorseSandboxUserInput!) {
endorseSandboxUser(input: $input) {
... on EndorseSandboxUserSuccessPayload {
sandboxUser {
id
lastName
firstName
nationalityCCA3
mobilePhoneNumber
birthDate
autoConsent
preferredNotificationChannel
verificationStatus
identificationStatus
isActive
idVerified
}
}
... on ForbiddenRejection {
message
__typename
}
... on SandboxUserNotFoundRejection {
message
userId
__typename
}
}
}
{
"input": {
"id": "{{YOUR_ID}}"
}
}
{
"data": {
"endorseSandboxUser": {
"sandboxUser": {
"id": "{{YOUR_SANBOX_USER_ID}}",
"lastName": "Doe",
"firstName": "Joe",
"nationalityCCA3": "FRA",
"mobilePhoneNumber": "+33673660996",
"birthDate": "2022-02-02",
"autoConsent": false,
"preferredNotificationChannel": null,
"verificationStatus": "Verified",
"identificationStatus": "ValidIdentity",
"isActive": true,
"idVerified": true,
"authenticators": [
{
"os": null,
"brand": null,
"model": null,
"type": "SwanWeb",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/97.0.4692.84 Mobile/15E148 Safari/604.1",
"acceptLanguage": null,
"status": "Enabled"
}
]
}
}
}
}
You can use this mutation to act as a different Sandbox user. For example, you can create a membership using the Sandbox User Alice, and accept the membership using Sandbox User Bob - all in the same flow.

Delete a sandbox user

If you created a sandbox user by error and want to delete it, use deleteSandboxUser.
This mutation must be called with a User Access Token from the Sandbox environment.
Request
Input
Response
mutation DeleteSandboxUser($input: DeleteSandboxUserInput!) {
deleteSandboxUser(input: $input) {
... on DeleteSandboxUserSuccessPayload {
success
}
... on ForbiddenRejection {
message
__typename
}
... on LastSandboxUserCannotBeDeletedRejection {
message
userId
__typename
}
... on SandboxUserNotFoundRejection {
message
userId
__typename
}
}
}
{
"input": {
"id": "{{YOUR_ID}}"
}
}
{
"data": {
"deleteSandboxUser": {
"success": true
}
}
}
This is possible only if you haven't yet used the sandbox user. A Sandbox user can't be deleted if they've already been used to accept a membership, create an account, consent, login, etc.
There is no real usage for this in Live since it's not possible to delete a user in real life.

Event simulator API

SEPA Credit Transfer

Receive an incoming Transfer
To create a new SepaCreditTransferIn transaction from outside of Swan toward a Swan account, call simulateIncomingSepaCreditTransferReception.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: IncomingSepaCreditTransferInput!) {
simulateIncomingSepaCreditTransferReception(input: $input) {
... on SimulateIncomingSepaCreditTransferReceptionSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
__typename
}
}
}
{
"input": {
"amount": {
"value": 10000,
"currency": "EUR"
},
"creditorAddress": {
"addressLine1": "Address line 1",
"addressLine2": "Address line 2",
"city": "Paris",
"postalCode": "75020",
"state": null,
"country": "FRA"
},
"creditorIban": "FR76 9999 9001 0012 4271 2152 906",
"creditorName": "Coucou account",
"debtorAddress": {
"addressLine1": "Address line 1",
"addressLine2": "Address line 2",
"city": "Paris",
"postalCode": "75020",
"state": null,
"country": "FRA"
},
"debtorIban": "FR2730003000706315734174B93",
"debtorName": "Debtor",
"endToEndId": "EtoE Ref",
"label": "Label to display"
}
}
{
"data": {
"simulateIncomingSepaCreditTransferReception": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to transfer money onto a newly created Swan account, to simulate a recurring SEPA coming into the Swan account, ...
Return an Incoming Transfer
To create a new SepaCreditTransferInReturn transaction, based on an original SepaCreditTransferIn transaction, call simulateIncomingSepaCreditTransferReturn
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateIncomingSepaCreditTransferReturnInput!) {
simulateIncomingSepaCreditTransferReturn(input: $input) {
... on SimulateIncomingSepaCreditTransferReturnSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByBeneficiary",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateIncomingSepaCreditTransferReturn": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
Try it now on the API Explorer
You can use this mutation to simulate when a SEPA Credit Transfer credits a Swan account and then Swan or the Swan debtor returned the funds.
Accept an Incoming Transfer Recall
To create a new SepaCreditTransferInRecall transaction, based on an original SepaCreditTransferIn transaction, call simulateIncomingSepaCreditTransferRecall
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateIncomingSepaCreditTransferAcceptedRecallInput!) {
simulateIncomingSepaCreditTransferAcceptedRecall(input: $input) {
... on SimulateIncomingSepaCreditTransferAcceptedRecallSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByOriginator",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateIncomingSepaCreditTransferAcceptedRecall": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a Sepa Credit Transfer credited a Swan account, the debtor bank or the debtor asked for a recall of the transaction, and then Swan or the Swan creditor accepted the recall request.
Book an outgoing transfer
To change the status of an outgoing SepaCreditTransferOut into Booked call simulateOutgoingSepaCreditTransferBooking .
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaCreditTransferBookingInput!) {
simulateOutgoingSepaCreditTransferBooking(input: $input) {
... on SimulateOutgoingSepaCreditTransferBookingSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"transactionId": "{{YOUR_TRANSACTION}}"
}
}
{
"data": {
"simulateOutgoingSepaCreditTransferBooking": {
"transactionId": "{{TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a SepaCreditTransferOut was initiated and is Pending, and you want to simulate the approval of the transfer.
Reject an outgoing transfer
To change the status of an outgoing SepaCreditTransferOut into Rejected call simulateOutgoingSepaCreditTransferRejection .
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaCreditTransferRejectionInput!) {
simulateOutgoingSepaCreditTransferRejection(input: $input) {
... on SimulateOutgoingSepaCreditTransferRejectionSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "BeneficiaryBankBicInvalid",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaCreditTransferRejection": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a SepaCreditTransferOut was initiated and is Pending, and you want to simulate the rejection of the transfer.
Receive an outgoing transfer return
To create a new SepaCreditTransferOutReturn transaction, based on an original SepaCreditTransferOut transaction, call simulateOutgoingSepaCreditTransferReturn
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaCreditTransferReturnInput!) {
simulateOutgoingSepaCreditTransferReturn(input: $input) {
... on SimulateOutgoingSepaCreditTransferReturnSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "BankOperationCodeInvalid",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaCreditTransferReturn": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a Sepa Credit Transfer debited a Swan account and the creditor bank or the creditor returned the funds.
Receive an Accepted Outgoing Transfer Recall
To create a new SepaCreditTransferOutRecall transaction, based on an original SepaCreditTransferOut transaction, call simulateOutgoingSepaCreditTransferAcceptedRecall
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaCreditTransferAcceptedRecallInput!) {
simulateOutgoingSepaCreditTransferAcceptedRecall(input: $input) {
... on SimulateOutgoingSepaCreditTransferAcceptedRecallSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByOriginator",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaCreditTransferAcceptedRecall": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a Sepa Credit Transfer debited a Swan account, Swan or the Swan debtor asked for a recall of the transaction, and then the creditor bank or the creditor accepted the recall request.

SEPA Direct Debit

Receive SDD instruction
To create a new SepaDirectDebitOut transaction call simulateOutgoingSepaDirectDebitReception. For SDD Core, the transaction will be Enabled, and SDD B2B the transaction will be in ConsentPending. Learn more about the consent process and mandate on here.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaDirectDebitReceptionInput!) {
simulateOutgoingSepaDirectDebitReception(input: $input) {
... on SimulateOutgoingSepaDirectDebitReceptionSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
}
}
{
"input": {
"amount": {
"value": 1000,
"currency": "EUR"
},
"creditor": {
"address": {
"addressLine1": "Address line 1",
"addressLine2": "Address line 2",
"city": "Paris",
"postalCode": "75020",
"state": null,
"country": "FRA"
},
"iban": "FR8814508000302986428271O12",
"name": "Creditor Inc",
"identifier": "FR11ABC123456",
"ultimateName": "UFC"
},
"debtorIban": "FR76 9999 9001 0012 4271 2152 906",
"endToEndId": "E2E ref",
"executionDate": "2022-02-10T16:00:00.000Z",
"mandateAmendment": {
"previousCreditorIdentifier": null,
"previousDebtorIban": null,
"previousMandateReference": null
},
"mandateReference": "8db058cc58ea4be792fef4370aa86e50",
"mandateSequence": "OneOff",
"mandateType": "Core",
"label": "Label SDD In"
}
}
{
"data": {
"simulateOutgoingSepaDirectDebitReception": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a Swan account receives a SEPA Direct Debit instruction and is debited at the executionDate.
Receive SDD instruction cancellation
To cancel a SepaDirectDebitOut transaction in Pending status, call simulateOutgoingSepaDirectDebitCancel.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaDirectDebitCancelInput!) {
simulateOutgoingSepaDirectDebitCancel(input: $input) {
... on SimulateOutgoingSepaDirectDebitCancelSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"data": {
"simulateOutgoingSepaDirectDebitCancel": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByOriginator",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
You can use this mutation to simulate a creditor canceling an incoming SEPA Direct Debit instruction sent to debit a Swan account, before the execution/debit date.
Receive a Direct Debit Refund
To create a new SepaDirectDebitOutReturn transaction, based on a SepaDirectDebitOut call simulateOutgoingSepaDirectDebitReturn.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaDirectDebitReturnInput!) {
simulateOutgoingSepaDirectDebitReturn(input: $input) {
... on SimulateOutgoingSepaDirectDebitReturnSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByPayer",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaDirectDebitReturn": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a SEPA Direct Debit instruction has been received and the transaction was booked on a Swan debtor account, then simulate a refund request by an authorized account member and the subsequent return of funds to their account.
Receive a Direct Debit Reversal
To create a new SepaDirectDebitOutReversal transaction, based on a SepaDirectDebitOut call simulateOutgoingSepaDirectDebitReverse.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaDirectDebitReverseInput!) {
simulateOutgoingSepaDirectDebitReverse(input: $input) {
... on SimulateOutgoingSepaDirectDebitReverseSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByPayer",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaDirectDebitReverse": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate when a SEPA Direct Debit instruction has been received and the transaction was booked on a Swan debtor account, then simulate the creditor reversing the transaction and the subsequent return of funds to the Swan account.
Reject a Direct Debit
To reject a SepaDirectDebitOut transaction in the Pending status, call simulateOutgoingSepaDirectDebitReject.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingSepaDirectDebitRejectInput!) {
simulateOutgoingSepaDirectDebitReject(input: $input) {
... on SimulateOutgoingSepaDirectDebitRejectSuccessPayload {
transactionId
}
... on ForbiddenRejection {
message
}
... on TransactionNotFoundRejection {
transactionId
message
}
}
}
{
"input": {
"reasonCode": "NotSpecifiedByPayer",
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateOutgoingSepaDirectDebitReject": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
​Try it now on the API Explorer. You can use this mutation to simulate a Swan debtor rejecting a SEPA Direct Debit instruction they've already received, before the execution/debit date.

Release a reserved balance

To simulate a transaction amount leaving the reserved balance and moving to the available balance, call simulateReleaseReservedBalance.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateReleaseReservedBalanceInput!) {
simulateReleaseReservedBalance(input: $input) {
... on ForbiddenRejection {
__typename
message
}
... on TransactionNotFoundRejection {
__typename
message
transactionId
}
... on ReleaseReservedBalanceSuccessPayload {
__typename
transactionId
}
}
}
{
"input": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
{
"data": {
"simulateReleaseReservedBalance": {
"transactionId": "{{YOUR_TRANSACTION_ID}}"
}
}
}
You can use this mutation to simulate the end of an account funding process.

Card payments

Create an authorization request from a merchant
To create a new CardOutAuthorization transaction in Pending status, call simulateOutgoingCardAuthorization.
This mutation can be called with a User Access Token or a Project Access Token.
Request
Input
Response
mutation Mutation($input: SimulateOutgoingCardAuthorizationInput!) {
simulateOutgoingCardAuthorization(input: $input) {
... on SimulateOutgoingCardAuthorizationSuccessPayload {
transactionId
}
... on SimulateOutgoingCardAuthorizationRejectPayload {
transactionId
reason
}
... on CardNotFoundRejection {
id
message
}
... on ForbiddenRejection {
message
}
}
}
{
"input": {
"amount": {
"value": 10,
"currency": "EUR"
},
"authorizationType": "Classic",
"cardId": "{{YOUR_CARD_ID}}",
"cardTransactionCategory": "InStore",
"cardType": "Virtual",
"merchant": {
"city": null,