Making your first payment

1. Get access to the sandbox

Ask your account manager or contact us at [email protected] to get access to your sandbox environment. The sandbox is a safe playground that is not connected to your bank accounts and can be used to safely develop and test your integration. It is pre-loaded with sample data.

2. Authenticate

The Numeral API uses API keys to authenticate requests. The API key must be passed in the X-API-Key HTTP header of your API calls.

3. Retrieve your connected account ID

A connected account is a bank account connected through Numeral. The ID of your connected account is necessary to create payment orders.

Use the List all connected accounts API endpoint to list connected accounts:

curl --request GET \
     --url 'https://sandbox.numeral.io/v1/connected_accounts' \
     --header 'Accept: application/json' \
     --header 'X-API-Key: Your_API_Key'

The API will return a list of your connected accounts:

{
  "records": [
    {
      "id": "634f662d-44f3-4b56-b823-a1442d5beb69",
      "type": "corporate",
      "name": "Checking account",
      "bic": "BNPAFRPPXXX",
      "account_number": "FR1420041010050500013M02606",
      "bank_id": "fd51a6d5-3fd4-4181-a9d0-5b46e1ac119a",
      "services_activated": [
        "sct"
      ],
      "metadata": {},
      "created_at": "2022-03-08T08:00:00Z",
      "disabled_at": null,
    }
  ]
}

4. Create a payment order

A payment order is an order to create a payment to or out of one of your connected accounts. Numeral connects to your bank to process this payment and sends status updates through webhooks.

Among other attributes, a payment order has a type, a direction, an amount, a currency, an origin account, and a receiving account.

Below is the request to send a €10.00 SEPA payment from the connected account 634f662d-44f3-4b56-b823-a1442d5beb69 (the origin account) to Acme Corporation's bank account with number FR9617569000505936314373V66 using the Create a payment order API endpoint:

curl --request POST \
     --url https://sandbox.numeral.io/v1/payment_orders \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'X-API-Key: Your_API_Key'
     --data '
{
  "type": "sepa",
  "direction": "credit",
  "amount": 2000,
  "currency": "EUR",
  "connected_account_id": "634f662d-44f3-4b56-b823-a1442d5beb69",
  "receiving_account": {
    "account_number": "FR9617569000505936314373V66",
    "bank_code": "NUMBFRPPXXX",
    "holder_name": "Acme Corporation",
    "holder_address": {
      "country": "FR"
    },
  },
  "reference": "Invoice #1234",
  "metadata": {
    "invoice_id": "b122-423f"
  }
}
'

📘

Indempotency key

In order to guarantee indempotency, Numeral most critical API endpoints support idempotency keys.

The payment order is created in the pending_approval status. The API will return the payment order object with its ID and additional details:

{
  "id": "b69837bf-e784-4bd1-b0d0-2ad0e61a89b6",
  "object": "payment_order",
  "connected_account_id": "254e3100-afd6-44f2-8084-87e8ae67b554",
  "type": "sepa",
  "status": "pending_approval",
  "direction": "credit",
  "amount": 2000,
  "currency": "EUR",
  "reference": "Invoice #1234",
  "originating_account": {
    "account_number": "FR7601234567890627967100010",
    "bank_code": "SOGEFRPP",
    "holder_name": "SoftwareCo",
    "holder_address": {
      "line_1": "1, rue Rivoli",
      "line_2": "",
      "postal_code": "75001",
      "region_state": "",
      "city": "Paris",
      "country": "FR"
    }
  },
  "receiving_account": {
    "account_number": "FR7601234567891127967100082",
    "bank_code": "BNPAFRPPXXX",
    "holder_name": "PartnerCo",
    "holder_address": {
      "line_1": "1, rue de la Bourse",
      "line_2": "",
      "postal_code": "59000",
      "region_state": "",
      "city": "Lille",
      "country": "FR"
    }
  },
  "value_date": "2022-01-06",
  "status_details": "",
  "reconciled_amount": 2000,
  "reconciliation_status": "reconciled",
  "reconciliation_override": false,
  "metadata": {
    "customer_id": "123"
  },
  "bank_data": {
    "file_id": null,
    "message_id": null,
    "transaction_id": null,
    "end_to_end_id": null
  },
  "created_at": "2022-01-06T11:53:19.734182Z",
  "updated_at": "2022-01-06T15:12:20.644356Z"
}

📘

Metadata

All Numeral API objects support metadata. The metadata object can be used to store your own internal IDs using a key:value pair.

5. Approve the payment order

curl --request POST \
     --url https://sandbox.numeral.io/v1/payment_orders/b69837bf-e784-4bd1-b0d0-2ad0e61a89b6/approve \
     --header 'Accept: application/json'
     --header 'X-API-Key: Your_API_Key'

Once a payment order has been created, you can approve it or reject it. To approve a payment order, use the Approve a payment order API endpoint:

The API will return the same payment order object with an approved status.

Similarly, you can use the Cancel a payment order API endpoint to cancel a payment order:

curl --request POST \
     --url https://sandbox.numeral.io/v1/payment_orders/b69837bf-e784-4bd1-b0d0-2ad0e61a89b6/cancel \
     --header 'Accept: application/json'
     --header 'x-api-key: YOUR_API_Key'

The API will return the same payment order object with a canceled status.

6. Validate the payment file

Every payment order approved is batched in a file with other payments to be sent to the bank. A file must be approved before being sent to the bank.

Two methods can be used to find a specific file to approve:

  1. By fetching a payment order object using the Retrieve a payment order API endpoint. A few seconds after being created, the payment order will be added to a file and its bank_data.file_id property will be filled with the actual file ID.
  2. By using the List all files API endpoint with the filter payment_id set to the actual payment order ID, and the status filter set to “created”.

Once you found the file ID, simply use the Approve a file API endpoint:

curl --request POST \
     --url https://sandbox.numeral.io/v1/files/a815e756-5576-4d40-9fbd-38ec304b856c/approve \
     --header 'Accept: application/json'
     --header 'x-api-key: YOUR_API_Key'

Once called, the above requests will send you back the actual file, which status will be set to “approved” and shortly after “sent”. Meanwhile, the status of the payment order will move from “approved” to “sent”.

Similarly, you can also use the Cancel a file API endpoint to cancel a file:

curl --request POST \
     --url https://sandbox.numeral.io/v1/files/a815e756-5576-4d40-9fbd-38ec304b856c/cancel \
     --header 'Accept: application/json'
     --header 'x-api-key: YOUR_API_Key'

7. Monitor the payment

Usually less than an hour after the payment has been sent to your bank, the bank sends back a payment status report to inform Numeral about the execution status of the payment. From this point, the payment could have been accepted or rejected.

To retrieve the latest status of a payment order, you can either retrieve the payment order or retrieve events.

1. Retrieve the payment order

You can use the Retrieve a payment order API endpoint at any time to monitor the ongoing status of your payment.

curl --request GET \
     --url 'https://sandbox.numeral.io/v1/payment_orders/b69837bf-e784-4bd1-b0d0-2ad0e61a89b6' \
     --header 'Accept: application/json'
     --header 'x-api-key: YOUR_API_Key'

If the status changes from sent to executed, it means that the payment has been executed by the bank. You can refer to the payment order lifecycle to learn more about status and possible status transitions.

2. Retrieve events

Similarly, you can use the List all events API endpoint to retrieve events related to your payment order and its file. You can use the related_object_id filter to filter events related to your payment ID or file ID:

curl --request GET \
     --url 'https://sandbox.numeral.io/v1/events?related_object_id=b69837bf-e784-4bd1-b0d0-2ad0e61a89b6' \
     --header 'Accept: application/json' \
     --header 'x-api-key: YOUR_API_Key'

The API will return all events related to your payment order or file.

8. Reconcile the payment

Numeral will automatically reconcile most of your payments with transactions coming from your account statements. However, if a payment could not be reconciled automatically, you can manually reconcile it.

To manually reconcile a payment, use the Create a reconciliation API endpoint with the transaction matching your payment (identified with the List all transactions or Retrieve a transaction API endpoints or by monitoring transaction-related events):

curl --request POST \
     --url https://sandbox.numeral.io/v1/reconciliations \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'x-api-key: YOUR_API_Key'
     --data '
{
  "payment_id": "b69837bf-e784-4bd1-b0d0-2ad0e61a89b6",
  "transaction_id": "519f922a-546d-4e66-975e-3269ea6e4c2c",
	"amount": 120,
	"metadata": {
	    "custom_service_id": "e784-4e66"
	}
}
'

To learn more about reconciliations, check our dedicated guide.

9. Bonus: use webhooks to receive events in real-time

Instead of manually refreshing the status of your payment order as described in step #7, you can subscribe to webhooks to automatically receive events.

Webhooks will automatically let your app know whenever changes happen to your objects. This is especially useful to automatically monitor the status changes of your payment orders and update your payment orders accordingly.