Get card information
const url = 'https://web.exactly.app/api/card';const options = {method: 'GET', headers: {cookie: 'credential_id=<credential_id>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://web.exactly.app/api/card \ --cookie credential_id=<credential_id>Retrieve the card profile, encrypted card data, and (optionally) a signature challenge for an authenticated user.
The sessionid header and the scope query parameter are independent and may be used together or separately:
- Provide
sessionidto receiveencryptedPan,encryptedCvc, andpin. Without it, only the card profile is returned. - Provide
scope=siweorscope=webauthnto receive achallengeto be signed and submitted viaPATCH /.siweandwebauthnare mutually exclusive within a single request.
Successful responses include push-provisioning credentials in the provisioning field only when the scope=provisioning query parameter is sent.
Retrieving encrypted card details
- Generate a session ID: Encrypt a 32‑character hexadecimal secret (no spaces/dashes) with the provided public RSA key using RSA‑OAEP.
- Send the request: Include the encrypted secret in the header
sessionidwhen calling this endpoint. - Decrypt the response: Use the original secret to decrypt
encryptedPan,encryptedCvc, andpin(each returned as{ data, iv }).
Requesting a signature challenge
Pass scope=siwe to receive a fully formed Sign-In with Ethereum message in challenge, or scope=webauthn to receive the plain authorization statement to be signed by a passkey. The signed result is submitted to PATCH / to bind the card to the user.
Step 1: Generate a sessionid and secret
import crypto from "node:crypto";
function session(): { sessionid: string; secret: string } {
const secret = crypto.randomUUID().replaceAll("-", "");
const secretKeyBase64 = Buffer.from(secret, "hex").toString("base64");
const secretKeyBase64Buffer = Buffer.from(secretKeyBase64, "utf8");
const secretKeyBase64BufferEncrypted = crypto.publicEncrypt(
{ key: pem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
secretKeyBase64Buffer,
);
return {
sessionid: secretKeyBase64BufferEncrypted.toString("base64"),
secret,
};
}
The sessionid is required to make an API request.
The secret will be needed for decryption later.
Step 2: Send the request
Use the sessionid in the header when calling this endpoint.
Step 3: Decrypt the response
Use the secret from Step 1 to decrypt the data.
import crypto from "node:crypto";
function decrypt(base64Secret: string, base64Iv: string, secretKey: string): string {
const secret = Buffer.from(base64Secret, "base64");
const iv = Buffer.from(base64Iv, "base64");
const decipher = crypto.createDecipheriv("aes-128-gcm", Buffer.from(secretKey, "hex"), iv);
decipher.setAutoPadding(false);
decipher.setAuthTag(secret.subarray(-16));
return Buffer.concat([decipher.update(secret.subarray(0, -16)), decipher.final()]).toString("utf8");
}
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Query Parameters
Section titled “Query Parameters”Responses
Section titled “Responses”Card information
object
object
object
object
object
object
Example
{ "cardId": "123e4567-e89b-12d3-a456-426655440000", "displayName": "John Doe", "expirationMonth": "12", "expirationYear": "2025", "lastFour": "1234", "mode": 0, "provider": "panda", "status": "ACTIVE", "limit": { "frequency": "per24HourPeriod" }, "productId": "408", "challenge": "1a2b3c", "provisioning": { "id": "card_abc123", "secret": "otp_xyz" }}Bad request
Forbidden
object
Example
{ "code": "no panda"}Not found
object
Example
{ "code": "no card"}