Physical Cards

Learn more about physical cards.

Encrypted PIN Block (Enterprise)

Physical cards can be provisioned for ATM/PIN debit access which requires a cardholder PIN. The Create card and Update card endpoints can set and update the cardholder PIN.


Due to the sensitive nature, PINs must be encrypted on the frontend immediately after user input.

An Encrypted PIN block is a JSON blob, encrypted with the Lithic API public key, base64 digest.

  "nonce": Integer,
  "pin": String
nonceA cryptographic nonce to create additional entropy and prevent replay attacks. This should be unique per request. Integer should be at least 8 digits in length.
pinCardholder PIN (between 4 and 12 numeric digits in length). String datatype to ensure leading zeros are not truncated.


import base64
import json
import random

# pip install pycryptodome
from Crypto.Cipher    import PKCS1_OAEP
from Crypto.PublicKey import RSA

pin_block =  {
    "nonce": random.randint(1e8, 1e12),
    "pin": "1234"

with open("path/to/") as f:
    cipher =

ciphertext = cipher.encrypt(json.dumps(pin_block).encode('utf-8'))

# Use this for the "pin" field value when creating or updating cards
encrypted_pin_block = base64.b64encode(ciphertext)
const fs = require("fs");

// npm install node-forge
const forge = require("node-forge");

const pem = fs.readFileSync("path/to/", "utf8");
const publicKey = forge.pki.publicKeyFromPem(pem);

function randomInt(low, high) {
  // Generate random integer between low and high, inclusive
  return Math.floor(Math.random() * (high - low + 1) + low)

const pinBlock =  {
  "nonce": randomInt(1e8, 1e12),
  "pin": "1234"

const ciphertext = publicKey.encrypt(JSON.stringify(pinBlock), "RSA-OAEP", {
  mgf1: {

// Use this for the "pin" field value when creating or updating cards
const encryptedPinBlock = forge.util.encode64(ciphertext);
package main

import (
    crand "crypto/rand"
    rand "math/rand"

type PinBlock struct {
    Nonce int    `json:"nonce"`
    Pin   string `json:"pin"`

func checkError(err error) {
    if err != nil {
        fmt.Println("Fatal error ", err.Error())

func loadKey(fileName string) (*rsa.PublicKey, error) {
    rawPem, err := ioutil.ReadFile(fileName)

    pemBlock, _ := pem.Decode(rawPem)

    return x509.ParsePKCS1PublicKey(pemBlock.Bytes)

func encrypt(msg []byte, publicKey *rsa.PublicKey) (string, error) {
    encryptedBytes, err := rsa.EncryptOAEP(
    if err != nil {
        return "", err
    encodedData := base64.StdEncoding.EncodeToString(encryptedBytes)
    return encodedData, nil

func generateNonce() int {
    min := int(1e8)
    max := int(1e12)
    return min + rand.Intn(max-min+1)

func main() {
    pemFile := "path/to/"

    publicKey, err := loadKey(pemFile)
    pinBlock := &PinBlock{Nonce: generateNonce(), Pin: "1234"}
    pinBlockBytes, err := json.Marshal(pinBlock)

    // Use this for the "pin" field value when creating or updating cards
    encoded, err := encrypt(pinBlockBytes, publicKey)


Reissue Card (Enterprise)

API reference: Reissue Card

Initiate print and shipment of a duplicate card. Only applies to cards of type PHYSICAL.


Sample Request

curl \
  -X POST \
  -H "Authorization: api-key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"card_token":"589e1412-b132-48ac-ab5c-befd02535a57"}'

Sample Response

  "created": "2021-06-28T22:58:36Z",
  "cvv": "162",
  "exp_month": "06",
  "exp_year": "2027",
  "funding": {
    "account_name": "Sandbox",
    "created": "2020-07-08 17:57:36",
    "last_four": "5263",
    "nickname": "",
    "state": "ENABLED",
    "token": "b0f0d91a-3697-46d8-85f3-20f0a585cbea",
  "hostname": "",
  "last_four": "5205",
  "memo": "",
  "pan": "4111111540285205",
  "spend_limit": 0,
  "spend_limit_duration": "TRANSACTION",
  "token": "589e1412-b132-48ac-ab5c-befd02535a57",
  "type": "PHYSICAL"
card_tokenThe unique token of the card to update
shipping_address (optional)Shipping address. If omitted, the previous shipping address will be used
product_id (optional)Alphanumeric identifier to specify physical card manufacturing attributes. Only applies to cards of type PHYSICAL. This must be configured with Lithic before use.

Did this page help you?