Skip to main content
The card number, CVV and expiry are never returned in plain text. They come back once, in encryptedCard on the issue response, encrypted to your own RSA public key — so only your private key can read them. Endl holds only the public key and cannot decrypt a card afterwards.
Shown once. A retry with the same Idempotency-Key returns 409 ERRCRD_1008 and no details, and there is no endpoint to fetch them again. Decrypt and use the card straight away, and store nothing you cannot afford to lose.

Generate your key pair

RSA, 2048 bits or more — 3072 recommended.

Register the public key

There is no API call for this. Share the public key with your Endl integration contact and Endl sets it up for your account. The private key never leaves your systems.
  1. Generate the pair with the commands above.
  2. Send card-public.pem — PEM, -----BEGIN PUBLIC KEY----- — plus an optional keyId label, up to 64 characters, such as acme-cards-2026.
  3. Endl confirms with the keyId and a fingerprint. Check it matches yours:
  4. Issue cards. Every encryptedCard carries the keyId it was encrypted to.
Without a registered key, issuing fails with 400 ERRCRD_1004.

Rotation

Send a new public key the same way. Once Endl registers it, it replaces the old one: cards issued afterwards use the new key and keyId. Keep the old private key until you no longer need cards issued before the switch.

How Endl encrypts each card

RSA-OAEP-256+A256GCM.
  1. Builds the plaintext {"pan":"4111111111112464","cvv":"123","expiryMonth":"12","expiryYear":"2030"}.
  2. Generates a fresh random 32-byte AES key and a 12-byte nonce, for this card only.
  3. Encrypts with AES-256-GCM, using the cardId as additional authenticated data (AAD) — so the payload is bound to this card.
  4. Encrypts the AES key with your RSA public key (RSA-OAEP, SHA-256).
  5. Returns encryptedKey, iv, ciphertext, tag, aad and the keyId, then wipes the AES key and card data from memory. Nothing is logged or stored.

How to decrypt

  1. RSA-OAEP (SHA-256, MGF1-SHA-256) decrypt base64 encryptedKey with your private key → a 32-byte AES key.
  2. AES-256-GCM decrypt base64 ciphertext with that key, nonce = base64 iv, auth tag = base64 tag, AAD = aad as UTF-8.
  3. Check aad equals cardId, and that pan ends with last4. A wrong key or tampered data fails the GCM check.
  4. Parse the JSON — pan, cvv, expiryMonth, expiryYear. Never log it.
Java’s GCM cipher expects ciphertext followed by the tag in one buffer, which is why the sample concatenates them. Node and Python take the tag separately.

The encryptedCard object