curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '
{
"approvals": [
{
"metadata": {
"authenticatorData": "<string>",
"challengeIndex": 123,
"clientDataJSON": "<string>",
"typeIndex": 123,
"userVerificationRequired": true
},
"signature": {
"r": "<string>",
"s": "<string>"
},
"signer": "<string>"
}
]
}
'import requests
url = "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals"
payload = { "approvals": [
{
"metadata": {
"authenticatorData": "<string>",
"challengeIndex": 123,
"clientDataJSON": "<string>",
"typeIndex": 123,
"userVerificationRequired": True
},
"signature": {
"r": "<string>",
"s": "<string>"
},
"signer": "<string>"
}
] }
headers = {
"X-API-KEY": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
approvals: [
{
metadata: {
authenticatorData: '<string>',
challengeIndex: 123,
clientDataJSON: '<string>',
typeIndex: 123,
userVerificationRequired: true
},
signature: {r: '<string>', s: '<string>'},
signer: '<string>'
}
]
})
};
fetch('https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'approvals' => [
[
'metadata' => [
'authenticatorData' => '<string>',
'challengeIndex' => 123,
'clientDataJSON' => '<string>',
'typeIndex' => 123,
'userVerificationRequired' => true
],
'signature' => [
'r' => '<string>',
's' => '<string>'
],
'signer' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals"
payload := strings.NewReader("{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals")
.header("X-API-KEY", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"chainType": "aptos",
"createdAt": 123,
"id": "<string>",
"onChain": {
"call": {
"data": "<unknown>",
"to": "<string>"
},
"explorerLink": "<string>",
"txId": "<string>"
},
"params": {
"call": {
"abi": [
"<unknown>"
],
"address": "<string>",
"args": [
"<unknown>"
],
"functionName": "<string>"
},
"chain": "abstract"
},
"status": "awaiting-approval",
"walletType": "mpc",
"approvals": {
"pending": [
{
"quorumApprovals": {
"pending": [
{
"message": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
}
}
],
"remaining": 123,
"submitted": [
{
"message": "<string>",
"signature": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
},
"submittedAt": 123
}
],
"threshold": 123
},
"signer": {
"locator": "<string>",
"type": "quorum"
}
}
],
"submitted": [
{
"quorumApprovals": {
"pending": [
{
"message": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
}
}
],
"remaining": 123,
"submitted": [
{
"message": "<string>",
"signature": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
},
"submittedAt": 123
}
],
"threshold": 123
},
"signer": {
"locator": "<string>",
"type": "quorum"
}
}
],
"required": 123
},
"completedAt": 123,
"error": {
"message": "<string>",
"reason": "program_error",
"logs": "<unknown>",
"revertData": {}
},
"fees": {
"mode": "project",
"actual": "<unknown>",
"billed": {
"usd": 123
},
"billing": {
"status": "failed"
},
"estimate": "<unknown>"
},
"sendParams": {
"params": {
"recipient": "<string>",
"recipientAddress": "<string>",
"amount": "<string>"
},
"token": "<string>"
}
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}Approve Transaction
Submit approval signature for a pending transaction. Required for transactions using external signers.
API scope required: wallets:transactions.sign
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '
{
"approvals": [
{
"metadata": {
"authenticatorData": "<string>",
"challengeIndex": 123,
"clientDataJSON": "<string>",
"typeIndex": 123,
"userVerificationRequired": true
},
"signature": {
"r": "<string>",
"s": "<string>"
},
"signer": "<string>"
}
]
}
'import requests
url = "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals"
payload = { "approvals": [
{
"metadata": {
"authenticatorData": "<string>",
"challengeIndex": 123,
"clientDataJSON": "<string>",
"typeIndex": 123,
"userVerificationRequired": True
},
"signature": {
"r": "<string>",
"s": "<string>"
},
"signer": "<string>"
}
] }
headers = {
"X-API-KEY": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
approvals: [
{
metadata: {
authenticatorData: '<string>',
challengeIndex: 123,
clientDataJSON: '<string>',
typeIndex: 123,
userVerificationRequired: true
},
signature: {r: '<string>', s: '<string>'},
signer: '<string>'
}
]
})
};
fetch('https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'approvals' => [
[
'metadata' => [
'authenticatorData' => '<string>',
'challengeIndex' => 123,
'clientDataJSON' => '<string>',
'typeIndex' => 123,
'userVerificationRequired' => true
],
'signature' => [
'r' => '<string>',
's' => '<string>'
],
'signer' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals"
payload := strings.NewReader("{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals")
.header("X-API-KEY", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.crossmint.com/api/2025-06-09/wallets/{walletLocator}/transactions/{transactionId}/approvals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"approvals\": [\n {\n \"metadata\": {\n \"authenticatorData\": \"<string>\",\n \"challengeIndex\": 123,\n \"clientDataJSON\": \"<string>\",\n \"typeIndex\": 123,\n \"userVerificationRequired\": true\n },\n \"signature\": {\n \"r\": \"<string>\",\n \"s\": \"<string>\"\n },\n \"signer\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"chainType": "aptos",
"createdAt": 123,
"id": "<string>",
"onChain": {
"call": {
"data": "<unknown>",
"to": "<string>"
},
"explorerLink": "<string>",
"txId": "<string>"
},
"params": {
"call": {
"abi": [
"<unknown>"
],
"address": "<string>",
"args": [
"<unknown>"
],
"functionName": "<string>"
},
"chain": "abstract"
},
"status": "awaiting-approval",
"walletType": "mpc",
"approvals": {
"pending": [
{
"quorumApprovals": {
"pending": [
{
"message": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
}
}
],
"remaining": 123,
"submitted": [
{
"message": "<string>",
"signature": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
},
"submittedAt": 123
}
],
"threshold": 123
},
"signer": {
"locator": "<string>",
"type": "quorum"
}
}
],
"submitted": [
{
"quorumApprovals": {
"pending": [
{
"message": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
}
}
],
"remaining": 123,
"submitted": [
{
"message": "<string>",
"signature": "<string>",
"signer": {
"address": "<string>",
"locator": "<string>",
"type": "api-key"
},
"submittedAt": 123
}
],
"threshold": 123
},
"signer": {
"locator": "<string>",
"type": "quorum"
}
}
],
"required": 123
},
"completedAt": 123,
"error": {
"message": "<string>",
"reason": "program_error",
"logs": "<unknown>",
"revertData": {}
},
"fees": {
"mode": "project",
"actual": "<unknown>",
"billed": {
"usd": 123
},
"billing": {
"status": "failed"
},
"estimate": "<unknown>"
},
"sendParams": {
"params": {
"recipient": "<string>",
"recipientAddress": "<string>",
"amount": "<string>"
},
"token": "<string>"
}
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}{
"error": true,
"message": "<error message>",
"code": "DEVICE_SIGNER_NOT_SUPPORTED"
}Headers
API key required for authentication
Path Parameters
A wallet locator can be of the format:
<walletAddress>email:<email>:<chainType>[:<walletType>][:alias:<alias>](walletType defaults to 'smart')userId:<userId>:<chainType>[:<walletType>][:alias:<alias>](white label user example)phoneNumber:<phoneNumber>:<chainType>[:<walletType>][:alias:<alias>]twitter:<handle>:<chainType>[:<walletType>][:alias:<alias>]x:<handle>:<chainType>[:<walletType>][:alias:<alias>]me:<chainType>[:<walletType>][:alias:<alias>](Use when calling from the client side with a client API key)chainType[:<walletType>]:alias:<alias>
Body
Array of approvals to submit for this transaction
1Schema for WebAuthn signature data
- Passkey signature submission payload
- Device signer signature submission payload
- Email signer signature submission payload
- External wallet signature submission payload
- Phone signer signature submission payload
- Server signer signature submission payload
Show child attributes
Show child attributes
Response
The approval has successfully been submitted to the transaction.
- EVM MPC Wallet
- EVM Smart Wallet
- Stellar Smart Wallet
- Solana Smart Wallet
- Solana MPC Wallet
Complete transaction response including status, signing requirements, and wallet type specific data
The blockchain type of the wallet
aptos, evm, solana, stellar, sui ISO timestamp when the transaction was created
Unique identifier for the transaction
Transaction data specific to EVM MPC wallets
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Current status of the transaction
awaiting-approval, failed, pending, success The wallet type (smart or mpc)
mpc, smart Complete approval data including requirements, pending and submitted signatures
Show child attributes
Show child attributes
ISO timestamp when the transaction reached finality
Error message if the transaction fails after submission
- Option 1
- Option 2
- Option 3
Show child attributes
Show child attributes
Transaction fee information including actual transaction fees, billed amounts, and billing status
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?

