Developer Quick Start
This guide walks through creating a redaction policy, redacting text, and redacting a PDF, using curl and Python.
Before You Start
You need a running Philter instance and an API key. Philter does not start without PHILTER_ENCRYPTION_KEY (see Encryption) and, on its first start, PHILTER_BOOTSTRAP_API_KEY, the first API key, which Philter seeds onto the admin user (see bootstrap settings). Running from the repository, ./compose.sh generates both into .env. Create narrower keys with the API Keys API.
Every request carries the key as a bearer token:
Authorization: Bearer <YOUR_API_KEY>
The examples use https://localhost:8080 and pass -k to curl because the Docker image generates a self-signed certificate on first start. Replace the host with your own endpoint, and drop -k once you install a trusted certificate.
Step 1: Create a Redaction Policy
A policy defines what Philter looks for and how each match is transformed. Philter ships with a default policy; this creates a custom one.
Save the following as my-policy.json:
{
"identifiers": {
"ssn": {
"ssnFilterStrategies": [
{ "strategy": "REDACT" }
]
},
"emailAddress": {
"emailAddressFilterStrategies": [
{ "strategy": "MASK" }
]
}
}
}
REDACT replaces the match with a label, and MASK replaces each character with a mask character. See Filter Strategies for the full set and Policy Schema for every field.
The policy name comes from the required name query parameter. If a policy with that name already exists, the request is refused with 409 and the reason policy_exists; replace an existing policy with PUT /api/policies/{name}.
Using curl
curl -k -X POST "https://localhost:8080/api/policies?name=my-custom-policy" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d @my-policy.json
A successful save returns 201 Created. An invalid policy returns 400 Bad Request with a message describing the problem, and nothing is stored.
Using Python
import requests
api_key = "YOUR_API_KEY"
base_url = "https://localhost:8080"
with open("my-policy.json") as f:
policy_json = f.read()
response = requests.post(
f"{base_url}/api/policies",
params={"name": "my-custom-policy"},
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
data=policy_json,
verify=False,
)
print(response.status_code)
Step 2: Redact Text
POST /api/filter with Content-Type: text/plain redacts text synchronously and returns the redacted text in the response body.
Two query parameters control the request:
p(optional, defaultdefault) is the policy to apply.c(optional) is the context. Documents sharing a context receive consistent replacements for the same value, so the same person can be tracked across documents without exposing their identity.
The examples use the default context created for a new account. If you deleted it, recreate it through the Contexts API or choose an existing context before continuing.
Using curl
curl -k -X POST "https://localhost:8080/api/filter?c=default&p=my-custom-policy" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: text/plain" \
-d "Send the results to jdoe@example.com and reference SSN 123-45-6789."
Send the results to **************** and reference SSN {{{REDACTED-ssn}}}.
The policy created in Step 1 covers only SSNs and email addresses, so nothing else in the text is changed. Person names, dates, and the rest require their own entries in the policy; the default policy is a broader starting point.
The response carries X-Philter-Policy-Name and X-Philter-Policy-Version recording which policy version governed the request, and X-Document-Id identifying the redaction (the ID of its ledger chain when the context has the ledger enabled). When output signing is enabled, or the request passes sign=true, an X-Philter-Signature JWT attests the response body.
Using Python
import requests
api_key = "YOUR_API_KEY"
base_url = "https://localhost:8080"
response = requests.post(
f"{base_url}/api/filter",
params={"c": "default", "p": "my-custom-policy"},
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "text/plain",
},
data="Send the results to jdoe@example.com and reference SSN 123-45-6789.",
verify=False,
)
print(response.text)
print(response.headers["X-Philter-Policy-Version"])
Step 3: Redact a PDF
POST /api/filter with Content-Type: application/pdf is asynchronous by default. Philter returns 202 Accepted with a documentId, redacts on a background worker, and holds the result for you to download. Append ?async=false to receive the redacted PDF inline instead.
The body may be up to MAX_FILE_SIZE_BYTES (10 MB by default); a larger body is rejected with 413 Payload Too Large. The body is also checked against the declared Content-Type, so a file that contradicts its header is rejected with 415 Unsupported Media Type rather than silently passed through the wrong pipeline.
Submit
curl -k -X POST "https://localhost:8080/api/filter?c=default&p=my-custom-policy" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/pdf" \
-H "Accept: application/pdf" \
--data-binary @sample_document.pdf
{"documentId":"c0c2c5a8-3a78-4e56-bf2a-44ad8b3a8e9f"}
Poll for Completion
curl -k -H "Authorization: Bearer YOUR_API_KEY" \
https://localhost:8080/api/documents/c0c2c5a8-3a78-4e56-bf2a-44ad8b3a8e9f/status
{"documentId":"c0c2c5a8-3a78-4e56-bf2a-44ad8b3a8e9f","status":"PROCESSING"}
A document moves through PENDING, PROCESSING, and then COMPLETE or FAILED. Rather than polling, you can configure a webhook to receive a signed notification when the redaction finishes; see Webhooks.
Download
curl -k -H "Authorization: Bearer YOUR_API_KEY" \
-o redacted_document.pdf \
https://localhost:8080/api/documents/c0c2c5a8-3a78-4e56-bf2a-44ad8b3a8e9f
Downloading before the redaction completes returns 409 Conflict; a failed redaction returns 410 Gone. Completed records are removed after PENDING_DOCUMENTS_TTL_SECONDS (7 days by default).
The Whole Flow in Python
import time
import requests
api_key = "YOUR_API_KEY"
base_url = "https://localhost:8080"
headers = {"Authorization": f"Bearer {api_key}"}
with open("sample_document.pdf", "rb") as f:
submit = requests.post(
f"{base_url}/api/filter",
params={"c": "default", "p": "my-custom-policy"},
headers={**headers, "Content-Type": "application/pdf", "Accept": "application/pdf"},
data=f,
verify=False,
)
document_id = submit.json()["documentId"]
while True:
status = requests.get(
f"{base_url}/api/documents/{document_id}/status",
headers=headers,
verify=False,
).json()["status"]
if status in ("COMPLETE", "FAILED"):
break
time.sleep(2)
if status == "COMPLETE":
download = requests.get(
f"{base_url}/api/documents/{document_id}",
headers=headers,
verify=False,
)
with open("redacted_document.pdf", "wb") as f:
f.write(download.content)
Validate the Output
Detection is probabilistic, so review the output against your own data before putting a policy into production. POST /api/explain returns the spans Philter identified and the policy that produced them, which is the fastest way to see why a value was or was not redacted. The Redaction Ledger records what was redacted in each document for later review.
Next Steps
- API Reference for every endpoint.
- Filtering API and Documents API for the full request and response details used above.
- Policy Schema for advanced policy configuration.
- Client SDKs for the Java SDK and for generating a client in other languages.
- The interactive Swagger UI on your own instance at
https://localhost:8080/swagger-ui/index.html.