Personal Identifiable Information (PII) guidelines
Last Updated: June 30, 2025
VA PII may never be stored on, processed on, or transmitted through non-VA assets.
❌ DO NOT email files containing PII to non-VA email addresses.
❌ DO NOT store or process PII on non-VA computers.
❌ DO NOT store PII within non-VA cloud services such as Google Drive, even temporarily.
PII CAN be stored and processed using:
✅ VA government-furnished equipment
✅ VA Azure Virtual Desktop or VA Citrix Access Gateway
✅ VA.gov AWS account
If you need to store or process PII but you cannot access approved methods, please reach out to platform support for assistance at #vfs-platform-support.
This document covers the rules, processes, and requirements for dealing with any personally identifiable information (PII) collected or stored by the VSP platform. These guidelines are intended to protect the privacy and security of VA.gov users and comply with federal privacy regulations.
OMB memorandum M-07-16 includes extensive details about securing PII, including this brief definition:
The term "personally identifiable information" refers to information which can be used to distinguish or trace an individual's identity, such as their name, social security number, biometric records, etc. alone, or when combined with other personal or identifying information which is linked or linkable to a specific individual, such as date and place of birth, mother’s maiden name, etc.
We expect developers to use the minimum amount of PII required by their application and be aware of where and how that data is stored throughout the web request lifecycle.
Examples of PII
To help identify and avoid improper handling of PII, developers can access a list of PII examples here:
https://depo-platform-documentation.scrollhelp.site/research-design/what-is-pii
If you need to store or process PII but you cannot access approved methods, please reach out to platform support for assistance in #vfs-platform-support
.
Avoid Logging Full response_body
Values
Logging full response_body
data — especially during error handling or debugging — can unintentionally expose sensitive Personally Identifiable Information (PII). We've observed cases where response payloads from internal VA services include user details such as:
ICNs
Full names
Addresses
Phone numbers
Email addresses
UUIDS
Other sensitive medical or identity information
Such logs not only pose a compliance risk (e.g., HIPAA, FedRAMP) but also make it difficult to control data propagation, even if the PII log table or Datadog's Sensitive Data Scanner is in place.
✅ Key Questions and Best Practices for Creating and Submitting Logs
Review response schemas from upstream services to understand what may be included.
Apply filters or serializers before logging payloads.
Redact or hash identifying values if necessary for trace correlation.
❌ Risky Logging Examples
These log entire payloads and may unintentionally include sensitive PII.
Rails.logger.error("Request failed with response body: #{response.body}")
Rails.logger.debug(response_body.to_json)
Rails.logger.info(response_body.to_json)
Rails.logger.warn(response_body.to_json)
✅ Safe Logging Examples
1. Use Structured Logging
Strongly recommended
Include data as structured key/value pairs. This improves searchability in log platforms and avoids accidental interpolation of unsafe strings.
Rails.logger.info("ExternalServiceCallFailed", {
service: "VAProfile",
status: response.status,
error_code: parsed["error_code"],
message: parsed["message"]
})
2. Log Only Non-PII Fields
Avoid raw string dumps. Instead, selectively log known-safe values from the response. Ensure fields are known to be safe.
Rails.logger.info("Service X request failed with status=#{response.status}")
parsed = JSON.parse(response.body)
safe_payload = parsed.slice('error_code', 'message')
Rails.logger.error("Service X error: #{safe_payload}")
3. Redact Sensitive Fields
ParameterFilterHelper can be used to scrub PII parameters or response.
def redact_pii_fields(params)
ParameterFilterHelper.filter_params(params)
end
Use a scrubber to sanitize logs if partial body is necessary. If a response includes useful data alongside PII, redact known sensitive keys.
def redact_pii_fields(data)
pii_keys = %w[ssn email phone icn address first_name last_name]
data.transform_keys(&:to_s).each_with_object({}) do |(key, value), result|
result[key] = pii_keys.include?(key) ? "[REDACTED]" : value
end
end
safe_payload = redact_pii_fields(JSON.parse(response.body))
Rails.logger.warn("Response (partial, redacted)", safe_payload)
def redact_pii_fields(response_body)
# whitelist only safe fields
body = JSON.parse(response_body)
body.except('ssn', 'email', 'phone', 'icn', 'address', 'first_name', 'last_name')
end
Rails.logger.warn("Partial response: #{redact_pii_fields(response.body)}")
4. Hash or Mask When Needed
The log includes a structured data field (icn_hash
) containing the SHA-256 hash of the ICN. Hashing here obscures the original ICN while still allowing consistent comparison (same input → same output).
require 'digest'
masked_icn = Digest::SHA256.hexdigest(parsed["icn"].to_s)
Rails.logger.info("Matched user by ICN hash", { icn_hash: masked_icn })
If Logging PII Is Required
Use the PII logging pathway, which is isolated, encrypted, and audited.
Apply tagging and lifecycle controls to avoid long-term retention of sensitive logs.
Existing capabilities
There are several points in the web request lifecycle where PII can be filtered or sanitized in the vets-api
application:
log filtering using the capabilities built into Rails
error report filtering in Sentry using a custom list of sanitized fields
This should ensure most PII is filtered automatically. If your application interacts with any new PII, especially from a new data source, you should ensure that the PII is filtered in both the Rails log and in any errors thrown by the application. You may need to add new PII fields to the Sentry PII sanitizer.
PII with the PersonalInformationLog
If you must log PII to implement a specific solution, Vets-API application does have a PersonalInformationLog
which is designed to store PII. This can be useful for tracking the data relating to specific errors involving PII, logging PII during the rollout of new features, or running scheduled tasks that require access to PII.
Any use of the PersonalInformationLog
will require additional review from the platform engineering team and you are advised to discuss the need for storing PII early in the development process.
Notes and policies regarding ICNs
Veterans with whom VA interacts, as well as beneficiaries, employees, IT users, healthcare providers, and others whom the VA interacts with, are assigned unique Integration Control Numbers (ICNs). The VA's authoritative identity service, which both stores identity information and assigns ICNs, is the Master Person Index (MPI).
Contrary to what you may have heard, ICNs are PII and should be treated as such. This means, among other things:
no storing them in systems not authorized to store PII, such as Datadog, Sentry, Google Analytics, and Domo;
no storing them in unencrypted database fields unless necessary to solve a technical problem for which no other solution is feasible;
no storing datasets containing them on non-GFE; and
no viewing or accessing them without a legitimate business purpose.
In addition, apps built on VA.gov should avoid using ICNs to link stored data to specific VA.gov users; the user_account_uuid
value should be used instead. More generally, ICNs should be stored in as few locations as possible.
Custom Logging and PII Filtering: Why Rails Filtering Isn’t Applied Automatically
By default, the Rails parameter filtering only protects standard Rails request logs. It does not automatically filter sensitive- or PII-related data in your custom log statements (such as those in manual Rails.logger.info
calls). To prevent accidental exposure of confidential information in custom logs, you must explicitly use the provided helper to filter parameters.
Custom Logging (Filtering NOT automatic!)
If you use
Rails.logger.info(params)
or custom log params in your own background jobs/services, the filter is NOT applied!You must use the helper:
RUBYParameterFilterHelper.filter_params(params)
How to Use the Filter in Custom Logs
Rails.logger.info("User params: #{ParameterFilterHelper.filter_params(params)}")
This ensures sensitive fields like passwords, SSNs, and sensitive file metadata are never exposed, regardless of where the log line is written.
How the PII Parameter Filter Works
Centralized logic: Sensitive data is redacted using the filter defined in
Rails.application.config.filter_parameters
Deep filtering: This logic recursively handles nested hashes, arrays, and uploaded files (
ActionDispatch::Http::UploadedFile
), redacting all fields except those in the allowlistAllowlist support: Only keys explicitly allowlisted are preserved in logs; all other sensitive values are scrubbed
Example: Before and After
Before (unsafe):
Rails.logger.info(params)
# Sensitive data (passwords, SSNs, etc.) might be logged!
After (safe):
Rails.logger.info(ParameterFilterHelper.filter_params(params))
# Sensitive fields will be replaced with [FILTERED]
Best Practices
Never log raw params hashes in custom logs
Always filter params with
ParameterFilterHelper.filter_params
The same filtering logic is shared between standard Rails logs and the helper for custom logs to guarantee consistent data redaction.
Rails does not filter sensitive data in custom logs by default.
Always use ParameterFilterHelper.filter_params
to prevent PII from leaking into logs outside of standard Rails request logging
Uniquely identifying users in logs
As noted above, a user's user_account_uuid
can be used to link stored data to specific VA.gov users. This includes in logs sent to Datadog, Sentry, etc., i.e., it is permissible to include the user_account_uuid
in log messages so that those logs can be linked back to the VA.gov user they're associated with.
From the fact that they are allowed to log user_account_uuid
values, developers sometimes erroneously conclude, "That must mean that user_account_uuid
values are not PII!" This is a common enough misconception that clarification is in order.
Any identifier which can be used to uniquely identify an individual is considered PII. There are two reasons why, despite this, we allow user_account_uuid
to be logged:
We are required by law and VA policy to limit our storage and use of PII to what is necessary to provide services. It would simply be too hard for us to operate VA.gov if it were impossible for us to ever associate log entries with specific users. That is, we need to be able to log some unique identifier, and this requirement is one of the reasons why
user_account_uuid
exists.Because
user_account_uuid
is randomly generated, i.e., there is no objective meaning about specific individuals that can be derived from it, and it is only used within VA.gov and its supporting systems such as Datadog. By design, its Confidentiality Impact Level is low, giving us more leeway in how it can be used.
An open source reminder
Much of the Platform code, including vets-api
and vets-website
is open source and available to anyone through GitHub. This means that you should be particularly careful about embedding anything that could be PII in your code, especially in tests.
Common PII that you should avoid:
Any data that uniquely identifies an individual such as SSN, ICN, EDIPI numbers
Any private settings configuration, in
settings.yml
for example
AI usage guidance
The growing use of AI tools like ChatGPT, GitHub Copilot, and others can enhance productivity—but they also introduce risks, especially in regulated environments.
AI tool usage: Be mindful when using AI tools like ChatGPT or GitHub Copilot. Avoid copying and pasting sensitive code, internal API keys, or user data into AI prompts. Vets-API is a public repository, any data shared with external models may not be secure or private.
GitHub Copilot has been enabled to assist with Vets-API pull request reviews. Since Copilot is powered by AI, the same usage and data protection guidelines outlined below apply.
Please follow these best practices when using AI:
Don’t share sensitive data
Never enter PII, internal API tokens, access credentials, or production logs into public AI tools.
Assume that data shared with public models may be stored or used for future training.
Review AI-generated code critically. AI can introduce insecure, non-compliant, or deprecated patterns—always review before committing. When in doubt, ask your tech lead or confirm with the Identity team.
If you have any questions about PII and protecting it, you should discuss it with your project lead or request help from the Platform in Slack.
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.