curl --request GET \
--url https://api.sumvin.com/v0/mandate-ceremonies/current \
--header 'x-juno-jwt: <api-key>'import requests
url = "https://api.sumvin.com/v0/mandate-ceremonies/current"
headers = {"x-juno-jwt": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-juno-jwt': '<api-key>'}};
fetch('https://api.sumvin.com/v0/mandate-ceremonies/current', 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://api.sumvin.com/v0/mandate-ceremonies/current",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-juno-jwt: <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"
"net/http"
"io"
)
func main() {
url := "https://api.sumvin.com/v0/mandate-ceremonies/current"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-juno-jwt", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.sumvin.com/v0/mandate-ceremonies/current")
.header("x-juno-jwt", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sumvin.com/v0/mandate-ceremonies/current")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-juno-jwt"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"_links": {
"decision": {
"href": "/v0/mandate-ceremonies/current/decision",
"method": "PUT"
},
"self": {
"href": "/v0/mandate-ceremonies/current"
}
},
"ceremony_id": "mcer-4f2a91c0",
"chain_id": 1329,
"expires_at": 1757337600000,
"mandate_uri": "sr:us:pint:9f14c2",
"scopes": [
"sr:us:pint:spend:visa_checkout?max=500.00¤cy=USD"
],
"signer_anchor": "registered_safe_owner",
"statement": "Authorise spending of up to 500.00 USD. This authorisation lapses on 2026-09-08T12:00:00Z.",
"status": "proposed",
"typed_data": {
"domain": {
"chainId": 1329,
"name": "Sumvin Purchase Intent",
"verifyingContract": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"version": "3"
},
"message": {
"conditions": [],
"expiresAt": 1757337600000,
"maxAmount": 0,
"maxAmountToken": "0x0000000000000000000000000000000000000000",
"nonce": 4,
"resources": [
"sr:us:person:safe:0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
],
"scopes": [
"sr:us:pint:spend:visa_checkout?max=500.00¤cy=USD"
],
"statement": "Authorise spending of up to 500.00 USD. This authorisation lapses on 2026-09-08T12:00:00Z.",
"wallet": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
},
"primaryType": "PurchaseIntent",
"types": {}
},
"verifying_contract": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}Read the mandate awaiting approval
Read the spending mandate an approval link refers to, so it can be shown to the account holder before they authorise it. Send the ticket from the link in the x-sumvin-ceremony-ticket header.
The response carries the prepared authorisation to sign, exactly as it is stored — sign those bytes unchanged. It also names the network, the address the signature is checked against, and what the signature has to prove, so nothing has to be reconstructed.
Call this as the signed-in account holder the mandate was prepared for. A link that has already been answered, or whose window has passed, reports that rather than offering a decision.
curl --request GET \
--url https://api.sumvin.com/v0/mandate-ceremonies/current \
--header 'x-juno-jwt: <api-key>'import requests
url = "https://api.sumvin.com/v0/mandate-ceremonies/current"
headers = {"x-juno-jwt": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-juno-jwt': '<api-key>'}};
fetch('https://api.sumvin.com/v0/mandate-ceremonies/current', 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://api.sumvin.com/v0/mandate-ceremonies/current",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-juno-jwt: <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"
"net/http"
"io"
)
func main() {
url := "https://api.sumvin.com/v0/mandate-ceremonies/current"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-juno-jwt", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.sumvin.com/v0/mandate-ceremonies/current")
.header("x-juno-jwt", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sumvin.com/v0/mandate-ceremonies/current")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-juno-jwt"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"_links": {
"decision": {
"href": "/v0/mandate-ceremonies/current/decision",
"method": "PUT"
},
"self": {
"href": "/v0/mandate-ceremonies/current"
}
},
"ceremony_id": "mcer-4f2a91c0",
"chain_id": 1329,
"expires_at": 1757337600000,
"mandate_uri": "sr:us:pint:9f14c2",
"scopes": [
"sr:us:pint:spend:visa_checkout?max=500.00¤cy=USD"
],
"signer_anchor": "registered_safe_owner",
"statement": "Authorise spending of up to 500.00 USD. This authorisation lapses on 2026-09-08T12:00:00Z.",
"status": "proposed",
"typed_data": {
"domain": {
"chainId": 1329,
"name": "Sumvin Purchase Intent",
"verifyingContract": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"version": "3"
},
"message": {
"conditions": [],
"expiresAt": 1757337600000,
"maxAmount": 0,
"maxAmountToken": "0x0000000000000000000000000000000000000000",
"nonce": 4,
"resources": [
"sr:us:person:safe:0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
],
"scopes": [
"sr:us:pint:spend:visa_checkout?max=500.00¤cy=USD"
],
"statement": "Authorise spending of up to 500.00 USD. This authorisation lapses on 2026-09-08T12:00:00Z.",
"wallet": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
},
"primaryType": "PurchaseIntent",
"types": {}
},
"verifying_contract": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "No wallet found with ID 12345 for this user.",
"error_code": "WAL-404-001",
"instance": "/v0/wallets/12345",
"status": 404,
"title": "Wallet Not Found",
"trace_id": "abc123-def456-ghi789",
"type": "https://api.sumvin.com/errors/wal-404-001"
}Authorizations
Session JWT from the identity provider that signed the user in — Dynamic Labs, Privy, or Clerk (consumer sign-in). Send it in the x-juno-jwt header on every authenticated request. An Authorization: Bearer <jwt> header carrying the same JWT is also accepted, and takes precedence when both are present.
Headers
Ticket from the approval link. Treat it as a secret: it addresses a live spending authorisation, carries exactly one decision, and stays usable until that decision is made or the approval window closes.
Tenant org ID for multi-tenant auth
Controls how timestamp fields are serialized in JSON response bodies.
Default (header omitted or any other value): epoch milliseconds as integers.
iso8601: UTC ISO 8601 strings of the form YYYY-MM-DDTHH:MM:SSZ.
Example: with X-Timestamp-Format: iso8601, the field value 1704067200000 becomes "2024-01-01T00:00:00Z".
Affected fields (recursively, in dicts and arrays): any field whose name ends in _at, plus the literal field names timestamp, period_start, and period_end. All other fields are passed through unchanged.
Only iso8601 is recognized. Any other value (or omitting the header) yields the default epoch-ms representation; the server does not reject unknown values, so this is documented as an example rather than an enum to keep generated clients permissive.
"iso8601"
Response
The mandate this approval link refers to
A proposed spending mandate as it currently stands.
HAL-style hypermedia links for navigation.
Show child attributes
Show child attributes
Stable identifier for this approval, safe to log and to quote in support.
Where the approval stands: awaiting a decision, authorised, declined, or lapsed. Reconciled against the mandate itself, which a separate actor can move.
proposed, stamped, declined, expired When this approval lapses, in epoch milliseconds. After it, nothing can be signed.
Identifier of the spending mandate being authorised.
The sentence the wallet shows the account holder. It names the spending limit and the moment the authorisation lapses, and it is part of what is signed.
Exactly what this mandate authorises, as it appears in the signed authorisation.
Network the signature is produced on.
Address the signature is checked against — the wallet on this account.
What the signature has to prove. registered_safe_owner means it must come from a key registered as an owner of the account's wallet; the wallet is a contract and cannot sign for itself.
claimed_wallet, registered_safe_owner The prepared authorisation to sign, exactly as stored. Sign these bytes unchanged — anything rebuilt from the fields above may differ from what is verified.
Show child attributes
Show child attributes
Why the approval reached this state when that is not simply the account holder's own answer — for example an authorisation withdrawn before it was approved. Null when there is nothing to add.