curl --request POST \
--url https://sis.sumvin.com/v0/sis/token/pint \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"pint": {
"wallet": "<string>",
"nonce": 123,
"statement": "<string>",
"scopes": [
"<string>"
],
"resources": [
"<string>"
],
"max_amount": "<string>",
"max_amount_token": "<string>",
"expires_at": 123,
"conditions": [
"<string>"
]
},
"signature": "<string>",
"audience": "<string>",
"source_chat_message_id": 123,
"enforcement_mode": "strict"
}
'import requests
url = "https://sis.sumvin.com/v0/sis/token/pint"
payload = {
"pint": {
"wallet": "<string>",
"nonce": 123,
"statement": "<string>",
"scopes": ["<string>"],
"resources": ["<string>"],
"max_amount": "<string>",
"max_amount_token": "<string>",
"expires_at": 123,
"conditions": ["<string>"]
},
"signature": "<string>",
"audience": "<string>",
"source_chat_message_id": 123,
"enforcement_mode": "strict"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
pint: {
wallet: '<string>',
nonce: 123,
statement: '<string>',
scopes: ['<string>'],
resources: ['<string>'],
max_amount: '<string>',
max_amount_token: '<string>',
expires_at: 123,
conditions: ['<string>']
},
signature: '<string>',
audience: '<string>',
source_chat_message_id: 123,
enforcement_mode: 'strict'
})
};
fetch('https://sis.sumvin.com/v0/sis/token/pint', 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://sis.sumvin.com/v0/sis/token/pint",
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([
'pint' => [
'wallet' => '<string>',
'nonce' => 123,
'statement' => '<string>',
'scopes' => [
'<string>'
],
'resources' => [
'<string>'
],
'max_amount' => '<string>',
'max_amount_token' => '<string>',
'expires_at' => 123,
'conditions' => [
'<string>'
]
],
'signature' => '<string>',
'audience' => '<string>',
'source_chat_message_id' => 123,
'enforcement_mode' => 'strict'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://sis.sumvin.com/v0/sis/token/pint"
payload := strings.NewReader("{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://sis.sumvin.com/v0/sis/token/pint")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sis.sumvin.com/v0/sis/token/pint")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}"
response = http.request(request)
puts response.read_body{
"_links": {
"self": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_status": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_tokens": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"revoke": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"jwks": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
}
},
"sig": "<string>",
"id": "<string>",
"audience": "<string>",
"scopes": [
"<string>"
],
"expires_at": 123,
"sri": "<string>"
}{
"_links": {
"self": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_status": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_tokens": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"revoke": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"jwks": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
}
},
"sig": "<string>",
"id": "<string>",
"audience": "<string>",
"scopes": [
"<string>"
],
"expires_at": 123,
"sri": "<string>"
}{
"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": "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"
}{
"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"
}Exchange a signed purchase intent for a token
Turn a purchase intent the account holder has signed into a token your organisation can verify.
Who the token is addressed to. On this endpoint audience must be your own organisation identifier — a token minted here is always addressed to the organisation the API key belongs to, so one that leaks cannot be presented by anyone else. Tokens addressed to a party outside Sumvin, named by a bare domain, are issued through the account holder’s own exchange rather than here, and carry only the identity attestations.
Re-sending an intent that has already been exchanged for the same audience returns the token already issued rather than minting a second.
One intent, several parties. The account holder’s own signed intent may be exchanged by more than one party, each receiving a separate token addressed to itself, without the wallet signing again. All of those tokens stop working together: when the intent expires or the account holder revokes it, every token issued against it is refused, whoever holds it. An intent reaches at most ten parties, after which a further exchange is refused and a newly signed intent is needed.
Because addressing a new party mints a new credential rather than repeating an existing one, the account holder’s standing is checked again at that moment — an intent signed while they were verified yields nothing further if their verification has since lapsed.
Verifying what you receive. The response’s jwks link gives the public keys to check the token’s signature against. Resolve the key by the token’s kid rather than pinning one key, since the signing keys rotate.
curl --request POST \
--url https://sis.sumvin.com/v0/sis/token/pint \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"pint": {
"wallet": "<string>",
"nonce": 123,
"statement": "<string>",
"scopes": [
"<string>"
],
"resources": [
"<string>"
],
"max_amount": "<string>",
"max_amount_token": "<string>",
"expires_at": 123,
"conditions": [
"<string>"
]
},
"signature": "<string>",
"audience": "<string>",
"source_chat_message_id": 123,
"enforcement_mode": "strict"
}
'import requests
url = "https://sis.sumvin.com/v0/sis/token/pint"
payload = {
"pint": {
"wallet": "<string>",
"nonce": 123,
"statement": "<string>",
"scopes": ["<string>"],
"resources": ["<string>"],
"max_amount": "<string>",
"max_amount_token": "<string>",
"expires_at": 123,
"conditions": ["<string>"]
},
"signature": "<string>",
"audience": "<string>",
"source_chat_message_id": 123,
"enforcement_mode": "strict"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
pint: {
wallet: '<string>',
nonce: 123,
statement: '<string>',
scopes: ['<string>'],
resources: ['<string>'],
max_amount: '<string>',
max_amount_token: '<string>',
expires_at: 123,
conditions: ['<string>']
},
signature: '<string>',
audience: '<string>',
source_chat_message_id: 123,
enforcement_mode: 'strict'
})
};
fetch('https://sis.sumvin.com/v0/sis/token/pint', 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://sis.sumvin.com/v0/sis/token/pint",
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([
'pint' => [
'wallet' => '<string>',
'nonce' => 123,
'statement' => '<string>',
'scopes' => [
'<string>'
],
'resources' => [
'<string>'
],
'max_amount' => '<string>',
'max_amount_token' => '<string>',
'expires_at' => 123,
'conditions' => [
'<string>'
]
],
'signature' => '<string>',
'audience' => '<string>',
'source_chat_message_id' => 123,
'enforcement_mode' => 'strict'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://sis.sumvin.com/v0/sis/token/pint"
payload := strings.NewReader("{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://sis.sumvin.com/v0/sis/token/pint")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sis.sumvin.com/v0/sis/token/pint")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"pint\": {\n \"wallet\": \"<string>\",\n \"nonce\": 123,\n \"statement\": \"<string>\",\n \"scopes\": [\n \"<string>\"\n ],\n \"resources\": [\n \"<string>\"\n ],\n \"max_amount\": \"<string>\",\n \"max_amount_token\": \"<string>\",\n \"expires_at\": 123,\n \"conditions\": [\n \"<string>\"\n ]\n },\n \"signature\": \"<string>\",\n \"audience\": \"<string>\",\n \"source_chat_message_id\": 123,\n \"enforcement_mode\": \"strict\"\n}"
response = http.request(request)
puts response.read_body{
"_links": {
"self": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_status": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_tokens": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"revoke": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"jwks": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
}
},
"sig": "<string>",
"id": "<string>",
"audience": "<string>",
"scopes": [
"<string>"
],
"expires_at": 123,
"sri": "<string>"
}{
"_links": {
"self": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_status": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"pint_tokens": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"revoke": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
},
"jwks": {
"href": "<string>",
"method": "GET",
"templated": false,
"description": "<string>"
}
},
"sig": "<string>",
"id": "<string>",
"audience": "<string>",
"scopes": [
"<string>"
],
"expires_at": 123,
"sri": "<string>"
}{
"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": "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"
}{
"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
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Response
Successful Response
Show child attributes
Show child attributes
JWT token issued for the PINT
PINT SRI (e.g. sr:us:pint:abc123)
Who the token is addressed to — your own organisation identifier
Authorized scopes for this PINT
Token expiry as epoch seconds
Sumvin Resource Identifier for the signer