Skip to main content
Skip table of contents

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:

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.

RUBY
Rails.logger.error("Request failed with response body: #{response.body}")
RUBY
Rails.logger.debug(response_body.to_json)
RUBY
Rails.logger.info(response_body.to_json)
RUBY
Rails.logger.warn(response_body.to_json)

✅ Safe Logging Examples

1. Use Structured Logging

(blue star) Strongly recommended

Include data as structured key/value pairs. This improves searchability in log platforms and avoids accidental interpolation of unsafe strings.

RUBY
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.

RUBY
Rails.logger.info("Service X request failed with status=#{response.status}")
RUBY
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.

RUBY
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.

RUBY
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)
RUBY
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).

RUBY
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:

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:

    RUBY
    ParameterFilterHelper.filter_params(params)

How to Use the Filter in Custom Logs

RUBY
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 allowlist

  • Allowlist support: Only keys explicitly allowlisted are preserved in logs; all other sensitive values are scrubbed

Example: Before and After

Before (unsafe):

RUBY
Rails.logger.info(params)
# Sensitive data (passwords, SSNs, etc.) might be logged!

After (safe):

RUBY
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:

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:

  1. 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.

  1. 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.


JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.