Send your first transfer

Send your first test transfer, from API keys to a webhook subscription.

By the end, you will have signed your requests with a JSON Web Signature (JWS), funded a test Account, and sent your first Transfer.

Step 1: Get your test API keys

In the Dashboard, go to Developers → API Keys. You'll see two key pairs: test (sk_test_...) and live (sk_live_...). Copy the test secret key.

Step 2: Register JWS keys

Money-moving endpoints (create transfer, return transfer, or verify a standardized Mexican bank account number (CLABE)) require each request to be signed. Follow Generate JWS keys to create a keypair and upload the public key. Come back here when you're done.

Step 3: List your Accounts

Every organization starts with a Root Account. Fetch it with a GET request to /v2/accounts:

curl --request GET \
     --url https://api.fintoc.com/v2/accounts \
     --header 'Authorization: YOUR_TEST_SECRET_KEY' \
     --header 'accept: application/json' \
     --header 'content-type: application/json'

The response should look like this:

[
  {
    "id": "acc_23JlasHas241",
    "object": "account",
    "mode": "test",
    "description": "My root account",
    "root_account_number": "000000000000000000",
    "root_account_number_id": "acno_Kasf91034gj1AD",
    "available_balance": 0,
    "currency": "mxn",
    "entity": {
      "id": "ent_4324qwkalsds",
      "holder_name": "ACME Inc.",
      "holder_id": "ND"
    }
  }
]

Using our SDK

If you're using Python or Node, install the Python SDK or Node SDK to call the API. The SDK signs each request with JWS and handles pagination.

First install our SDK:

npm install fintoc
pip install fintoc

Now list your accounts:

const { Fintoc } = require('fintoc');

// You can provide a path to your PEM file or pass the PEM key directly as a string.
// const privateKey = process.env.JWS_PRIVATE_KEY;
const privateKey = './private_key.pem';

const fintoc = new Fintoc('YOUR_TEST_SECRET_KEY', privateKey);

const accounts = await fintoc.v2.accounts.list();
for await (const account of accounts) {
  console.log(account.id, account.description, account.root_account_number, account.available_balance);
}
from fintoc import Fintoc

# You can provide a path to your PEM file or pass the PEM key directly as a string.
# jws_private_key = os.environ.get('JWS_PRIVATE_KEY')
jws_private_key = "./private_key.pem"

client = Fintoc("YOUR_TEST_SECRET_KEY", jws_private_key=jws_private_key)

for account in client.v2.accounts.list():
  print(account.id, account.description, account.root_account_number, account.available_balance)

Keep the id (acc_...) and root_account_number_id (acno_...) from the response. You use root_account_number_id to fund the test Account, and id to create the outbound Transfer.

Step 4: Fund your test Account

Your test Account starts empty. Simulate an inbound transfer so you have money to send:

curl --request POST \
     --url https://api.fintoc.com/v2/simulate/receive_transfer \
     --header 'Authorization: YOUR_TEST_SECRET_KEY' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "account_number_id": "acno_Kasf91034gj1AD",
  "amount": 59013,
  "currency": "mxn"
}
'

The response is the simulated inbound Transfer that funds your Account:

{
  "id": "tr_7HbN2kPq9wZ4xR1s",
  "object": "transfer",
  "amount": 59013,
  "currency": "mxn",
  "status": "succeeded",
  "direction": "inbound",
  "account_number_id": "acno_Kasf91034gj1AD",
  "comment": "Simulated inbound transfer"
}
🚧

Currencies are represented as integers

The Fintoc API represents money in the smallest currency unit as an integer with no decimals. MXN $590.13 is 59013. CLP has no minor unit, so a transfer of $59,013 CLP is also 59013.

See Currencies.

More simulation recipes (failures, returns, rejected payouts) live in Test your integration.

Step 5: Send your first Transfer

curl --request POST \
     --url https://api.fintoc.com/v2/transfers \
     --header 'Authorization: YOUR_TEST_SECRET_KEY' \
     --header 'Fintoc-JWS-Signature: YOUR_JWS_SIGNATURE' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "amount": 59013,
  "currency": "mxn",
  "account_id": "acc_23JlasHas241",
  "comment": "Pago de credito 10451",
  "reference_id": "150195",
  "counterparty": {
    "account_number": "000000000000000000"
  }
}'

The response is the outbound Transfer you created:

{
  "id": "tr_5pQ8rXcViBnM3kL2",
  "object": "transfer",
  "amount": 59013,
  "currency": "mxn",
  "status": "succeeded",
  "direction": "outbound",
  "comment": "Pago de credito 10451",
  "reference_id": "150195",
  "counterparty": {
    "account_number": "000000000000000000"
  }
}

Step 6: Create a webhook endpoint

Webhooks notify your server when a transfer settles, so you do not poll for status. Go to Developers → Webhooks in the Dashboard, add your URL, and subscribe to transfer.inbound.succeeded and transfer.outbound.succeeded.

Test the integration

Confirm the full flow worked end to end:

  1. After Step 4, list your Accounts again and confirm available_balance increased by 59013.
  2. Confirm the Step 5 transfer returns "status": "succeeded".
  3. Confirm your endpoint receives a transfer.outbound.succeeded webhook for the transfer.