# PCI scope: how to keep card data out of your servers

URL: https://www.paydude.io/resources/pci-scope-keeping-card-data-out-of-your-servers
Type: Guide
Published: 2026-09-24 · Updated: 2026-09-24
Summary: What brings a system into PCI scope, how hosted fields keep your servers out of it, and the common accidents that quietly expand your scope again.

## Summary

Scope follows the data: storing, processing or transmitting a card number brings a system in. Forwarding a card number without saving it still counts as transmitting it. Hosted fields keep the number out of your systems entirely — the browser sends it to the processor directly. That is the difference between a short self-assessment and a much longer one. The common accidents are a phone-order endpoint, a request logger, and a migration script.

PCI DSS is a long document, and most of it is about protecting card data you hold. The useful realisation for a developer is that the cheapest way to satisfy a requirement about card data is to not have any — and that this is an architectural choice you make in a handful of places, not a compliance exercise you do afterwards.

## What brings a system into scope

Three verbs: **store**, **process**, **transmit**. Any system that does one of them to cardholder data is in your cardholder data environment, along with anything connected to it that could affect its security.

The word that catches people is *transmit*. An endpoint that receives a card number and immediately forwards it to a processor — writing nothing, keeping nothing — is transmitting cardholder data. It is in scope. So is the load balancer in front of it, the logging pipeline behind it, and the error tracker that might capture a request body.

> **Not storing it is not the same as not handling it:** The most common misunderstanding in this area is that scope is about your database. It is about the data path. A card number that exists in your process memory for four milliseconds was still transmitted by a system you operate, and every system it passed through on the way is in scope with it.

## Hosted fields, and why they work

The way out is to arrange for the card number never to reach you. Hosted fields do this by serving the actual inputs from the processor: each one is an iframe on the processor origin, positioned inside your layout so it looks like part of your form.

```javascript
const form = await payments.form({ payment_method_type: "card" });

// Your divs. The processor renders the real inputs inside them, from its
// own origin — so the same-origin policy, not a promise, is what stops your
// JavaScript from reading what the customer types.
form.createField("cardNumber").inject("#card-number");
form.createField("cardExpiry").inject("#card-expiry");
form.createField("cardCvv").inject("#card-cvv");
await form.build();

// What comes back is a reference and display metadata. There is no method
// here that returns the card number, by design.
const paymentMethod = await payments.createPaymentMethod({
  type: "card",
  billing_details: { name: "Ada Lovelace", address: { zip: "80021", country: "US" } },
});
// -> { id: "pm_…", card: { brand: "visa", last4: "1111", exp_month, exp_year } }
```

The card goes from the iframe straight to the processor. Your server sees a `pm_…` identifier and four digits. There is no point in the flow at which a system you operate holds the number, which is why this arrangement reduces your obligation rather than merely satisfying it.

## What that changes on paper

**How the integration style changes the assessment**

| Integration | Card data touches your servers? | Typical questionnaire |
| --- | --- | --- |
| Processor-hosted fields or redirect | No | SAQ A — the shortest |
| Your own form posting to the processor from the browser | No, but you control the payment page | SAQ A-EP — substantially longer |
| Your server receives and forwards the card | Yes | SAQ D — the full questionnaire |
| Your server stores card numbers | Yes, and at rest | SAQ D, plus storage and encryption controls |

The gap between the first and third rows is the entire point of this article. It is not a difference of degree — it is the difference between a short self-assessment and an obligation that realistically needs dedicated effort to maintain. And it is decided by where a single form input is served from.

> **Which questionnaire applies is not your call alone:** Your acquirer determines your validation requirements, and they depend on transaction volume as well as integration style. Treat the table above as the shape of the decision rather than as your answer — the point is that the architecture is what moves you between rows, and the architecture is the part you control.

## The accidents that pull you back in

Scope is not set once. It is re-decided every time someone adds a feature, and each of these is usually added for a sensible reason by someone who has not thought about scope.

1. **A phone-order endpoint.** Support needs to take a card over the phone, so someone adds a form that posts the number to your API. That single endpoint moves your servers into the cardholder data environment. Use a processor-hosted virtual terminal instead.
2. **A generic request logger.** Body logging on an endpoint that receives card data writes card numbers into your logs, which are replicated, shipped to a third party and retained for a year.
3. **An error tracker capturing request payloads.** Same failure, worse reach: the payload now sits with a vendor outside your control.
4. **A migration script.** Importing card data from a previous provider puts the script, the machine it runs on and the file it reads in scope. Ask both providers to run a processor-to-processor migration instead — the card data never lands with you.
5. **A CSV from a partner.** Any file containing card numbers is in scope from the moment it arrives, including in the inbox it arrived in.
6. **A screenshot in a support ticket.** Not a code path, but a real one, and your ticketing system is not designed to be a cardholder data environment.

> **The migration one is the expensive surprise:** Switching processors is exactly when someone proposes exporting card data and importing it. It feels like a one-off with no lasting consequence, and it brings your infrastructure into scope for the duration plus however long the export file survives in a backup. Providers run token migrations between themselves precisely so you do not have to touch the data — ask for that path before anyone writes a script.

## What is still yours

Keeping card data out removes the largest part of the obligation, not the whole of it. What remains is worth knowing, because it is the part a good integration still has to get right.

- **The page that loads the payment form.** A compromised script on your checkout page can overlay fake inputs on top of the hosted ones. Content Security Policy and integrity attributes on third-party scripts are the defences.
- **Your API keys.** The secret key stays server-side and out of the client bundle. In a Next.js app that means never giving it a `NEXT_PUBLIC_` prefix — that prefix inlines the value into the browser bundle at build time.
- **Access control.** Who on your team can see payment records, issue refunds, or read the customer data attached to them.
- **Your vendors.** Anything in the path of the checkout page — a tag manager, an analytics snippet, a session recorder — is part of your exposure. Session recorders are the notable hazard, since capturing an iframe is exactly their job.
- **The self-assessment itself.** Shorter, but not absent, and it has to be completed.

> **A rule that keeps the decision simple:** If a card number could appear in a request to a system you operate, stop and redesign before writing the handler. Every expansion of scope described above began with someone writing that endpoint first and thinking about compliance afterwards, and unwinding it later costs far more than routing around it on the day.

**Built to keep you out of scope** Hosted fields, SDKs and an open sandbox on every tier. — [View pricing](https://www.paydude.io/pricing)

## Frequently asked questions

### What actually puts my server in PCI scope?

Storing, processing or transmitting cardholder data. A raw card number arriving at your endpoint counts as transmitting it, even if you forward it immediately and never write it down. Scope follows the data, not your intentions for it.

### What is the difference between SAQ A and SAQ A-EP?

Both are for merchants who do not handle card data directly, but they differ by how much of the payment page you control. SAQ A applies when the payment form is wholly served by a third party, such as hosted fields in an iframe or a redirect. SAQ A-EP applies when your own page controls elements of the payment form, which brings in many more requirements. Hosted fields are what keeps most integrations in the shorter questionnaire.

### Does using a token mean I am out of scope entirely?

It removes the largest part of your obligation but not all of it. You still have responsibilities around the page that loads the payment form, your keys, your access controls and your vendors. What it removes is the cardholder data environment — the part that requires storage controls, network segmentation and evidence of how card numbers are handled.

### Is it safe to log the request body on my payment endpoint?

Only if you are certain no card data can be in it, which with hosted fields it cannot. The danger is a generic request logger on a route that does receive card data, because logs are replicated, shipped to third parties and retained far longer than anything you would deliberately store. Log the token, never the payload.

### Do I still need to worry about PCI if my processor handles everything?

Yes, in the reduced form. You are still responsible for the integrity of the page that loads the payment form, for keeping your API keys secret, and for completing the appropriate self-assessment questionnaire. What a good integration buys you is a much shorter questionnaire, not an absent one.

### What is the one change most likely to expand my scope by accident?

Adding a server endpoint that accepts card details — usually written for a good reason, such as a phone order taken by a support agent or a migration from another provider. That single endpoint moves your servers into the cardholder data environment. Route those cases through a processor-hosted form instead.
