How PCI Proxy Works
You send us card data. We tokenise it, store it in a PCI DSS vault and return a token. Your systems never receive the card number.
You send
We vault it
You receive
The flow in 5 steps
From the moment the customer enters their card to the token you save in your back office: here is what happens step by step.
Card data in
Website checkout, API call or call centre form
PCI Proxy intercepts
We receive card data from the HTTP payload before it reaches your servers
Tokenisation
We encrypt the card number and generate a unique token
Secure vault
The card number stays encrypted in our PCI DSS Level 1 vault, EU only
Token to you
You receive the token and use it for payments, subscriptions or refunds
What happens when we tokenise
We replace the card number with a token. You never see the plain card in your systems. Here are the three internal steps.
Step
We detect card data
We analyse incoming requests and identify card numbers in JSON, forms or multipart payloads. No code changes on your side: connect your flow and you are done.
Step
We create the token
Every token starts with tok_pci_eu_ and includes the last four digits, scheme and expiry. So your UI can show "Visa ending in 1234" without ever holding the card number.
Step
Same token, same card
If the same card arrives again, we return the same token. Useful for subscriptions and saved cards. The mapping stays only in the vault and is not exposed via API.
→ POST /v1/tokenize
{
"card_number": "4111 1111 1111 1234",
"expiry": "12/26"
}
← 200 OK · 47ms
{
"token": "tok_pci_eu_a1b2c3d4e5f61234",
"last_four": "1234",
"brand": "visa",
"card_in_your_systems": false
}
When the card number is needed
To authorise a payment the PSP needs the real card number. You send the token: we retrieve the card from the vault and forward it securely. You never see it.
When it happens
When you need to charge a saved card, send the token to our forward endpoint. We resolve the card number in the vault and pass it to the PSP over an encrypted connection. It does not appear in your logs.
Who can do it
Only holders of an API key with forward or detokenize permission. You can restrict by IP, environment and request volume.
Fully audited
Every retrieval is logged: who requested it, when, and which PSP. Logs are kept for at least 12 months and are available from the dashboard or API.
Protection layers
Every request is authenticated
Only from your authorised servers
TLS with verified certificates
Retained for at least 12 months
Two endpoints, clear flow
Authenticate with your API key. One endpoint creates the token, the other uses it to pay. JSON in, JSON out.
/v1/tokenize Scope: tokenize Request body
{ "card_number": "4111111111111111", "expiry": "12/26", "cvv": "123" }
Response 200 OK
{ "token": "tok_pci_eu_a1b2c3d4e5f6", "last_four": "1111", "brand": "visa", "expires_at": "2026-12-31" }
/v1/forward Scope: forward Request body
{ "token": "tok_pci_eu_a1b2c3d4e5f6", "target_url": "https://psp.eu/charge", "amount": 9900, "currency": "EUR" }
Response (from PSP, proxied)
{ "status": "authorized", "transaction_id": "txn_9f8e7d6c", "amount": 9900, "currency": "EUR" }
Webhooks and notifications
When something happens (token created, payment sent, error), we send a real-time event to your endpoint. Every message is signed so you can verify it server-side.
If delivery fails, we retry up to 5 times with increasing intervals. You can also replay manually from the dashboard within 72 hours.
Supported Event Types
token.created New token generated token.used Token sent to PSP token.expired Token reached its TTL token.deleted Token removed on request forward.success PSP returned 2xx forward.failure PSP returned error Webhook Payload Example
your-server.com/webhooks/pci LIVE
{
"event": "token.created",
"timestamp": "2026-04-03T10:15:30Z",
"data": {
"token": "tok_pci_eu_a1b2c3d4e5f6",
"last_four": "1111",
"brand": "visa",
"merchant_id": "mrc_xyz789"
},
"signature": "sha256=4a8b9c..." // HMAC-SHA256
} Signature verification
Every webhook includes the X-PCI-Signature header. Compute HMAC-SHA256 of the body with your secret and compare it to the signature. If they do not match, reject the request.
SDKs and ways to integrate
SDKs for Node, Python and PHP, or hosted fields in iFrames. Choose what fits your stack.
JavaScript SDK
npmimport PCIProxy from '@pci-proxy-eu/js';
const pci = new PCIProxy({
merchantId: 'mrc_xyz789'
});
const { token } = await pci.tokenize({
cardNumber: '4111...'
}); Python SDK
PyPIfrom pci_proxy_eu import Client
client = Client(
merchant_id="mrc_xyz789",
api_key="sk_live_..."
)
result = client.tokenize(
card_number="4111..."
) PHP SDK
Composeruse PCIProxyEU\Client;
$client = new Client(
merchantId: 'mrc_xyz789',
apiKey: 'sk_live_...'
);
$result = $client->tokenize([
'card_number' => '4111...'
]); Secure iFrames for checkout
To stay in SAQ A: card fields are rendered in our iFrames. Card data never passes through your DOM.
<div id="card-number"></div>
<div id="card-expiry"></div>
<div id="card-cvv"></div>
<script>
PCIProxy.hostedFields({
merchantId: 'mrc_xyz789',
fields: {
cardNumber: '#card-number',
expiry: '#card-expiry',
cvv: '#card-cvv'
},
onTokenize: (result) => {
// token only, never the card number
console.log(result.token);
}
});
</script> Payment Details
Card data never on your server
SAQ A
Less PCI burden
CSS
Customisable styling
Mobile
Responsive
Ready to integrate?
Learn what a PCI Proxy is, explore tokenisation in depth or see how developers use the platform.