Skip to main content

Overview

Iron requires email two-factor authentication (2FA) for sensitive actions. This checks that actions cannot be performed on behalf of a customer without their explicit approval. 2FA is required in the following cases:
  • Editing the destination address of an Autoramp (fiat or wallet).
  • Registering a fiat address that needs authorization.
  • Reusing an existing vIBAN or account number (for example, cancelling and re-creating an Autoramp that reuses the same vIBAN).
  • Changing a customer’s email address.
During customer onboarding, Iron collects verified email addresses from individuals legally associated with the customer. 2FA emails go to these addresses. When an action triggers 2FA, the affected entity (Autoramp, fiat address, or email change) enters the AuthorizationRequired state. It stays there until the customer confirms the action.

How it works

1

Customer triggers a sensitive action

Your app calls an endpoint that requires 2FA, such as editing an Autoramp destination. The entity moves to the AuthorizationRequired state and Iron emails the customer a 2FA code with a confirmation link.
2

Customer confirms via link or code

If the customer clicks the confirmation link in the email, the action completes and no further work is needed. If they don’t, they can enter the code manually in your app.
3

Fetch the pending authentication code

Look up the pending authentication code by the entity’s ID to get the authentication code ID you need for submission.
curl -X GET "https://api.sandbox.iron.xyz/api/authentication-codes/entity/{entity_id}" \
-H "X-API-Key: $API_KEY"
4

Submit the code

Submit the code the customer entered against the authentication code ID from the previous step.
curl -X PUT "https://api.sandbox.iron.xyz/api/authentication-codes/{auth_code_id}" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{ "code": 123456 }'
5

Check the result

A successful submission returns the authentication code with "status": "Confirmed" and the entity leaves the AuthorizationRequired state.

Get the pending authentication code

GET /api/authentication-codes/entity/{id} Returns the pending authentication code for an entity in the AuthorizationRequired state. The {id} is the ID of the entity itself (the Autoramp, fiat address, or email change), not the authentication code.
curl -X GET "https://api.sandbox.iron.xyz/api/authentication-codes/entity/4b85d15e-f343-41c0-809c-85314cae2fa6" \
-H "X-API-Key: $API_KEY"

Response

{
  "id": "9f1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "status": "Pending",
  "attempts": 0,
  "entity_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
  "expires_at": "2025-01-15T10:45:00Z"
}
FieldTypeDescription
idUUIDThe authentication code ID. Use it to submit the code.
statusstringOne of Pending, Expired, Confirmed, Rejected
attemptsintegerNumber of submission attempts so far
entity_idUUIDThe entity this authentication relates to
expires_attimestampWhen the code expires
If no pending code exists for the entity, this endpoint returns 404 Not Found. This happens when the code expired or was already confirmed.

Submit an authentication code

PUT /api/authentication-codes/{id} Submits the code the customer received by email. The {id} is the authentication code ID from the previous endpoint.
curl -X PUT "https://api.sandbox.iron.xyz/api/authentication-codes/9f1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{ "code": 123456 }'

Request body

FieldTypeRequiredDescription
codeintegerYesThe 2FA code from the customer’s email
code is an integer, not a string. Send 123456, not "123456". A string value fails validation.

Response

Returns the updated authentication code. Check status to confirm the result:
{
  "id": "9f1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  "status": "Confirmed",
  "attempts": 1,
  "entity_id": "4b85d15e-f343-41c0-809c-85314cae2fa6",
  "expires_at": "2025-01-15T10:45:00Z"
}

Authentication code statuses

StatusDescription
PendingThe code was sent and is waiting for confirmation.
ConfirmedThe customer confirmed the action. The entity leaves AuthorizationRequired.
ExpiredThe code passed its expires_at time without confirmation.
RejectedThe submitted code was wrong too many times.

Sub-partner scoping

Both endpoints accept an optional X-SUB-PARTNER-ID header. When provided, the authentication code lookup and submission are scoped to that sub-partner.

Common errors

ErrorCause
401 UnauthorizedMissing or invalid X-API-Key
404 Not FoundNo pending authentication code for this entity, or the authentication code ID doesn’t exist
500 Internal ErrorReturns { "message": "...", "trace_id": "..." }. Reference the trace_id when reporting issues.
The endpoints POST /api/autoramps/{autoramp_id}/retry-auth and POST /api/addresses/fiat/{id}/retry-auth are deprecated. Don’t build on them. If no pending code exists for an entity, re-trigger the underlying action instead.

Sandbox testing

In sandbox, the static code 123456 confirms any pending authentication. Submit it as an integer:
{ "code": 123456 }

Autoramps

Fiat Addresses