curl --request POST \
--url https://api.baselayer.com/issuance/credentials \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-API-Key: <api-key>' \
--data '
{
"audience": "<string>",
"agent_key": {
"x": "<string>",
"kty": "OKP",
"crv": "Ed25519",
"kid": "<string>"
},
"individual_ref": "<string>",
"type": "INDIVIDUAL",
"format": "dc+sd-jwt",
"level": "L3",
"ttl_seconds": 1,
"disclosed_fields": [
"<string>"
],
"disclosure_mode": "CLEARTEXT_AND_HASH"
}
'import requests
url = "https://api.baselayer.com/issuance/credentials"
payload = {
"audience": "<string>",
"agent_key": {
"x": "<string>",
"kty": "OKP",
"crv": "Ed25519",
"kid": "<string>"
},
"individual_ref": "<string>",
"type": "INDIVIDUAL",
"format": "dc+sd-jwt",
"level": "L3",
"ttl_seconds": 1,
"disclosed_fields": ["<string>"],
"disclosure_mode": "CLEARTEXT_AND_HASH"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-API-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
audience: '<string>',
agent_key: {x: '<string>', kty: 'OKP', crv: 'Ed25519', kid: '<string>'},
individual_ref: '<string>',
type: 'INDIVIDUAL',
format: 'dc+sd-jwt',
level: 'L3',
ttl_seconds: 1,
disclosed_fields: ['<string>'],
disclosure_mode: 'CLEARTEXT_AND_HASH'
})
};
fetch('https://api.baselayer.com/issuance/credentials', 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.baselayer.com/issuance/credentials",
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([
'audience' => '<string>',
'agent_key' => [
'x' => '<string>',
'kty' => 'OKP',
'crv' => 'Ed25519',
'kid' => '<string>'
],
'individual_ref' => '<string>',
'type' => 'INDIVIDUAL',
'format' => 'dc+sd-jwt',
'level' => 'L3',
'ttl_seconds' => 1,
'disclosed_fields' => [
'<string>'
],
'disclosure_mode' => 'CLEARTEXT_AND_HASH'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-API-Key: <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://api.baselayer.com/issuance/credentials"
payload := strings.NewReader("{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-API-Key", "<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://api.baselayer.com/issuance/credentials")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.baselayer.com/issuance/credentials")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}"
response = http.request(request)
puts response.read_body{
"environment": "sandbox",
"ttl_capped": true,
"credentials": [
{
"role": "subject",
"credential_id": "<string>",
"credential": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"disclosed_fields": [
"<string>"
],
"active_keys": [
"<string>"
]
}
]
}{
"type": "resource_not_found",
"code": 1,
"message": "Could not locate the resource for the given URI.",
"metadata": {}
}{
"type": "<string>",
"code": 123,
"message": "<string>",
"metadata": {},
"request_id": "<string>"
}{
"type": "invalid_credential_mint_payload",
"code": 6303,
"message": "The credential mint payload failed validation.",
"metadata": {}
}Mint a credential for a person or a business operator
Mints an L2 or L3 SD-JWT-VC credential for a verified individual, referenced by the individual_ref issued to your organization, scoped to a counterparty audience, and bound to the presenting agent’s public key. type chooses whose credential: INDIVIDUAL for the person as themselves, BUSINESS for the person acting as the operator of the business business_ref names, which is never inferred and requires a verified operator link. Every credential is issuer-signed with sub (the audience-scoped pairwise DID of the person or business, stable across mints and levels) and aud (the canonical audience). A business L3 mint answers with two credentials, the business’s and the actor’s. Credentials are short-lived projections of current verification: mint on demand rather than storing them long-term. Send an Idempotency-Key: a retry with the same key and body within 24 hours answers with the same credentials, marked Idempotent-Replayed: true, instead of minting again.
curl --request POST \
--url https://api.baselayer.com/issuance/credentials \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-API-Key: <api-key>' \
--data '
{
"audience": "<string>",
"agent_key": {
"x": "<string>",
"kty": "OKP",
"crv": "Ed25519",
"kid": "<string>"
},
"individual_ref": "<string>",
"type": "INDIVIDUAL",
"format": "dc+sd-jwt",
"level": "L3",
"ttl_seconds": 1,
"disclosed_fields": [
"<string>"
],
"disclosure_mode": "CLEARTEXT_AND_HASH"
}
'import requests
url = "https://api.baselayer.com/issuance/credentials"
payload = {
"audience": "<string>",
"agent_key": {
"x": "<string>",
"kty": "OKP",
"crv": "Ed25519",
"kid": "<string>"
},
"individual_ref": "<string>",
"type": "INDIVIDUAL",
"format": "dc+sd-jwt",
"level": "L3",
"ttl_seconds": 1,
"disclosed_fields": ["<string>"],
"disclosure_mode": "CLEARTEXT_AND_HASH"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-API-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
audience: '<string>',
agent_key: {x: '<string>', kty: 'OKP', crv: 'Ed25519', kid: '<string>'},
individual_ref: '<string>',
type: 'INDIVIDUAL',
format: 'dc+sd-jwt',
level: 'L3',
ttl_seconds: 1,
disclosed_fields: ['<string>'],
disclosure_mode: 'CLEARTEXT_AND_HASH'
})
};
fetch('https://api.baselayer.com/issuance/credentials', 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.baselayer.com/issuance/credentials",
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([
'audience' => '<string>',
'agent_key' => [
'x' => '<string>',
'kty' => 'OKP',
'crv' => 'Ed25519',
'kid' => '<string>'
],
'individual_ref' => '<string>',
'type' => 'INDIVIDUAL',
'format' => 'dc+sd-jwt',
'level' => 'L3',
'ttl_seconds' => 1,
'disclosed_fields' => [
'<string>'
],
'disclosure_mode' => 'CLEARTEXT_AND_HASH'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-API-Key: <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://api.baselayer.com/issuance/credentials"
payload := strings.NewReader("{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-API-Key", "<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://api.baselayer.com/issuance/credentials")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.baselayer.com/issuance/credentials")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"audience\": \"<string>\",\n \"agent_key\": {\n \"x\": \"<string>\",\n \"kty\": \"OKP\",\n \"crv\": \"Ed25519\",\n \"kid\": \"<string>\"\n },\n \"individual_ref\": \"<string>\",\n \"type\": \"INDIVIDUAL\",\n \"format\": \"dc+sd-jwt\",\n \"level\": \"L3\",\n \"ttl_seconds\": 1,\n \"disclosed_fields\": [\n \"<string>\"\n ],\n \"disclosure_mode\": \"CLEARTEXT_AND_HASH\"\n}"
response = http.request(request)
puts response.read_body{
"environment": "sandbox",
"ttl_capped": true,
"credentials": [
{
"role": "subject",
"credential_id": "<string>",
"credential": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"disclosed_fields": [
"<string>"
],
"active_keys": [
"<string>"
]
}
]
}{
"type": "resource_not_found",
"code": 1,
"message": "Could not locate the resource for the given URI.",
"metadata": {}
}{
"type": "<string>",
"code": 123,
"message": "<string>",
"metadata": {},
"request_id": "<string>"
}{
"type": "invalid_credential_mint_payload",
"code": 6303,
"message": "The credential mint payload failed validation.",
"metadata": {}
}Authorizations
Headers
An opaque key, ideally a UUID, that makes this request safe to retry. A second request with the same key and body within 24 hours answers with the first one's response, marked Idempotent-Replayed: true, without minting again. The same key with a different body is refused with 422, and a retry that arrives while the first is still running with 409. Keys are scoped to your application.
Body
- IndividualCredentialMintRequest (v1)
- BusinessCredentialMintRequest (v1)
type: INDIVIDUAL: mint for a verified person as themselves.
The counterparty the credential is scoped to: a bare domain (login.example.com), or an https authorization-server identifier (https://login.example.com) kept whole, which derives a different pairwise subject than the bare host. Paths, queries, fragments and non-default ports are refused.
1 - 512Public JWK of the agent that will present the credential; becomes the credential's cnf binding.
Show child attributes
Show child attributes
Pairwise reference to the verified individual, as issued to the calling organization at verification completion.
1 - 255INDIVIDUAL: the person as themselves.
"INDIVIDUAL"Credential wire format. dc+sd-jwt (media type application/dc+sd-jwt, draft-ietf-oauth-sd-jwt-vc-19 §2.1) is the only format issued.
dc+sd-jwt Credential layer to mint (defaults to L3): L2 (audience-scoped pairwise DID) or L3 (selectively disclosed verified identity).
L2, L3 Maximum credential validity in seconds, capped at supporting evidence expiry. Omitted or null uses the server default (3600 seconds). Must not exceed the configured maximum (86400 seconds by default). Applies to L2 and L3.
x > 0L3 only: the dotted disclosure paths the subject credential should carry, e.g. email or hashes.email. A business mint lists business paths here and the actor credential's paths, e.g. given_name, in actor_disclosed_fields.
64L3 only: disclose a use case's fields instead of listing disclosed_fields; never both. GET /issuance/credentials/presets lists each preset's paths. A preset always shares values in the clear, and B2B presets need a business mint.
consumer_ecommerce, age_gated, consumer_regulated, b2b_ecommerce, b2b_regulated L3 disclosure mode: CLEARTEXT_AND_HASH (default) discloses requested fields as cleartext alongside their recognition hashes; HASH_ONLY discloses only the recognition hashes.
CLEARTEXT_AND_HASH, HASH_ONLY Response
The minted credentials.
Body of POST /issuance/credentials: one credential for an individual, and
for a business L3 mint the subject's followed by the actor's.
The environment the credentials were minted in.
sandbox, production Whether supporting evidence expiry shortened the requested validity; each entry's expires_at is the instant it ends.
The minted credentials, subject first.
Show child attributes
Show child attributes