Skip to content

Contexts API

The Contexts API provides endpoints for retrieving, creating, updating, and deleting contexts, and for listing, exporting, and importing the token-to-replacement mappings within a context.

Admin cross-user access: by default each endpoint operates on the calling user's own contexts. Because context names are unique only per user, an admin identifies another user's context by adding an owner=<username> query parameter. A non-admin that names another user as owner, or an owner that does not exist, receives 404 Not Found. Cross-user access is disabled by default; enable it with ADMIN_CROSS_USER_ACCESS_ENABLED=true (see Settings). While disabled, naming another user as owner also returns 404 Not Found. A deactivated user may be named as owner: deactivation keeps their data, and an admin reaches it as for an active user.

Philter serves HTTPS on port 8080 with a generated self-signed certificate by default, so the curl examples on this page pass -k. See the TLS settings to supply your own certificate.

Get Context Names

Method Endpoint Description
GET /api/contexts List contexts (paginated).

Returns the contexts on the requested page in contexts and total, how many there are across every page. See Listings for paging, sorting, and searching. Each item is an object with the context's name and when it was created (createdAt). Its settings and size are returned by Get Context Details.

Query Parameters

  • offset (optional, default: 0) - The number of contexts to skip.
  • limit (optional, default: 25, max: 100) - The maximum number of contexts to return.
  • q (optional) - Only contexts whose name contains q, ignoring case.
  • sort (optional, default: name) - name or created.
  • order (optional, default: asc) - asc or desc.
  • owner (optional) - The username of the user whose contexts to list. When omitted, the caller's own contexts are listed. Supplying an owner other than yourself requires admin privileges and cross-user access being enabled (ADMIN_CROSS_USER_ACCESS_ENABLED=true; disabled by default); otherwise it receives 404 Not Found.
  • all_users (optional, default: false) - List every user's contexts instead of the caller's. Each item then also has its owner's username. Requires an administrator and ADMIN_CROSS_USER_ACCESS_ENABLED=true (disabled by default), as owner does; otherwise it returns 404 Not Found. Cannot be combined with owner.

Example request:

curl -k -H "Authorization: Bearer <token>" "https://localhost:8080/api/contexts?offset=0&limit=100"

Example response:

{
  "contexts": [
    { "name": "default", "createdAt": "2026-10-01T12:00:00.000Z" },
    { "name": "my-context", "createdAt": "2026-10-05T10:20:30.000Z" }
  ],
  "total": 2
}

Get Context Details

Method Endpoint Description
GET /api/contexts/{name} Get the details of a context, where {name} is the name of the context to get.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to get. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

Example request:

curl -k -H "Authorization: Bearer <token>" https://localhost:8080/api/contexts/my-context

Example response:

{
  "size": 125,
  "filterTypes": {
    "email-address": 40,
    "person": 83
  },
  "untyped": 2,
  "entityTypeDisambiguation": true,
  "disambiguationScope": "document",
  "ledger": false
}
  • entityTypeDisambiguation - Whether entity type disambiguation is enabled for the context.
  • disambiguationScope - document or context: whether disambiguation learns within each document or across the context. See disambiguationScope under Create a Context.
  • ledger - Whether the context records redactions in the ledger.
  • size - The number of entries in the context.
  • filterTypes - The number of entries for each filter type, sorted by filter type.
  • untyped - The number of entries with no filter type. Only an import can create one.

The counts are computed in the database in one query and sum to size.

Create a Context

Method Endpoint Description
POST /api/contexts Create a new context.

Request Body

A JSON object. The settings use the names Get Context Details returns, and one left out takes its default.

  • name (required) - The name of the context to create. Context names are unique per user: if you already have a context with this name the request is rejected with 409 Conflict. A name you use does not prevent another user from using the same name. Each user can have at most 10 contexts; see Capacity. Because the name is how the context is addressed in a request path, it cannot contain /, \, ;, %, or control characters, and cannot be . or ...
  • entityTypeDisambiguation (optional, default: false) - Whether to enable entity type disambiguation for this context.
  • disambiguationScope (optional, default: document) - What span disambiguation learns from, when entityTypeDisambiguation is enabled. Case does not matter.
    • document - Each document is disambiguated on its own, from what Philter learns in that document. Nothing is stored.
    • context - What Philter learns is stored in Philter's database and used for every later document redacted in the context, up to MAX_VECTORS_PER_CONTEXT vectors. Stored vectors are kept if the scope or disambiguation changes, and deleted when the context is emptied or deleted.
  • ledger (optional, default: false) - Whether to enable the redaction ledger for this context.

The name and settings are not accepted as query parameters, and a request that sends one there is refused with 400.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to create the context for. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

Responses

  • 201 Created - The context was created.
  • 400 Bad Request - The name is missing or blank or breaks the rule above, disambiguationScope is not document or context, or the name or a setting is sent as a query parameter. The body carries a message, and a field naming what was refused.
  • 409 Conflict - The context was not created because of your existing contexts. The body's reason says which, so a client need not read the message:
    • context_exists - You already have a context with this name.
    • context_limit_reached - You already have 10 contexts.
{
  "message": "Maximum number of contexts reached.",
  "reason": "context_limit_reached"
}

Example request:

curl -X POST -k -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  "https://localhost:8080/api/contexts" \
  -d '{"name": "my-context", "entityTypeDisambiguation": true, "disambiguationScope": "context"}'

Update a Context

Method Endpoint Description
PATCH /api/contexts/{name} Change a context's entityTypeDisambiguation, disambiguationScope, and ledger settings.

Only the settings given change. A setting left out keeps its current value, so changing one never changes another. Read the current values with Get Context Details.

Request Body

A JSON object with the settings to change. The names are those Get Context Details returns.

  • entityTypeDisambiguation (optional) - Whether to enable entity type disambiguation. Left out, it is unchanged.
  • disambiguationScope (optional) - document or context, as for Create a Context. Left out, it is unchanged.
  • ledger (optional) - Whether to enable the redaction ledger. Left out, it is unchanged; turning it off stops recording redaction evidence for the context.

The settings are not accepted as query parameters, and a request that sends one there is refused with 400.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to update. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

Example request:

curl -X PATCH -k -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  "https://localhost:8080/api/contexts/my-context" -d '{"entityTypeDisambiguation": false, "ledger": true}'

Returns 200 OK on success, 400 Bad Request if no setting is given, disambiguationScope is not document or context (nothing changes, including the other settings given), or a setting is sent as a query parameter, and 404 Not Found if no context with that name exists for the calling user. Each change is recorded as a context_updated audit event naming the settings whose values changed.

Delete a Context

Method Endpoint Description
DELETE /api/contexts/{name} Delete a context, where {name} is the name of the context to delete.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to delete. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

A delete is rejected with 409 Conflict if any asynchronously-submitted document referencing the context is still pending or processing. Either wait for the jobs to finish (poll the Documents API) or delete the pending jobs first.

Responses

  • 204 No Content - The context was deleted.
  • 404 Not Found - No context with that name exists for the user (the body's message is Context does not exist.), or the owner does not exist or may not be reached.
  • 409 Conflict - The context has pending or processing asynchronous jobs.

Example request:

curl -X DELETE -k -H "Authorization: Bearer <token>" https://localhost:8080/api/contexts/my-context

List Context Entries

Method Endpoint Description
GET /api/contexts/{name}/entries Paginated list of token-to-replacement mappings.

Query Parameters

  • offset (optional, default: 0)
  • limit (optional, default: 25, max: 100)
  • sort (optional, default: created) - created, newest first by default, or reads. See Listings.
  • order (optional) - asc or desc.
  • owner (optional, admin only) - Username of another user whose context to list entries from. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

Example request:

curl -k -H "Authorization: Bearer <token>" \
  "https://localhost:8080/api/contexts/my-context/entries?offset=0&limit=25"

Example response:

{
  "entries": [
    {
      "id": "6a1101d50737f1edfcf70d77",
      "replacement": "{{{REDACTED-person}}}",
      "filterType": "PERSON",
      "reads": 14,
      "timestamp": "2026-05-22T20:00:00.000Z"
    }
  ],
  "total": 1
}

The original token is never returned by this endpoint; only a keyed hash of it is stored.

Empty a Context

Method Endpoint Description
DELETE /api/contexts/{name}/entries Remove all entries from a context.

Emptying a context also deletes the span-disambiguation vectors stored for it under the context disambiguation scope. Its settings are unchanged.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to empty. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

Removes every token-to-replacement mapping in the context but leaves the context itself in place. Returns 204 No Content on success, 404 Not Found if the context does not exist.

curl -X DELETE -k -H "Authorization: Bearer <token>" \
  https://localhost:8080/api/contexts/my-context/entries

Delete a Single Entry

Method Endpoint Description
DELETE /api/contexts/{name}/entries/{entryId} Remove one entry by its id.

Query Parameters

  • owner (optional, admin only) - Username of another user whose context to delete the entry from. Requires cross-user access to be enabled; otherwise it returns 404 Not Found.

entryId is the id returned in the entries listing. Returns 204 No Content if deleted, 404 Not Found if no such entry exists for the calling user, 400 Bad Request if entryId is not a valid id.

curl -X DELETE -k -H "Authorization: Bearer <token>" \
  https://localhost:8080/api/contexts/my-context/entries/6a1101d50737f1edfcf70d77

Export a Context's Mapping Table

Method Endpoint Description
GET /api/contexts/{name}/entries/export Export the complete mapping table for a context as JSON.

Exports every token-to-replacement mapping in the context, at most MAX_CONTEXT_SIZE entries, in a JSON form that can be re-imported into another of the same user's contexts, in this deployment or another with the same PHILTER_ENCRYPTION_KEY and a user with the same username (see Import below). Imported anywhere else, it will not match the original tokens during redaction.

Authorization: only the user that created the context or an admin may export it. Any other caller receives 404 Not Found, which is also returned when the context does not exist. The endpoint does not reveal the existence of a context you are not allowed to access.

Query Parameters

  • owner (optional) - The username of the context's owner. Because context names are unique only per user, an admin uses this to identify another user's context unambiguously. When omitted, the export applies to the caller's own context with the given name. Supplying an owner other than yourself requires admin privileges and cross-user access being enabled (ADMIN_CROSS_USER_ACCESS_ENABLED=true; disabled by default); otherwise it receives 404 Not Found.

Returns 200 OK with the export document. The response is sent with a Content-Disposition header so it can be saved directly to a file.

Security: the export contains a keyed hash (HMAC-SHA256) of each original token, never the original value, along with its replacement. The key is derived from PHILTER_ENCRYPTION_KEY and the context owner's username and never leaves the deployment, so the hash cannot be reversed by guessing candidate values, and another user cannot match it by redacting guesses in a context of their own. That matters because most of what Philter detects is low-entropy: an SSN is only 109 possibilities, a date of birth far fewer. A bare digest of one is recoverable by enumeration in seconds. The export still reveals the replacement values, so treat it as sensitive and transmit/store it securely.

Portability: because the hash is keyed to the deployment's key and the owner's username, an export matches only in contexts owned by the same username: another of that user's contexts, or a context in another installation with the same PHILTER_ENCRYPTION_KEY whose user has that username. Imported into another user's context, the hashes do not match, and the original tokens are not stored, so they cannot be recomputed. An administrator moving a user's mappings imports them into that user's own context, with owner.

Example request:

curl -k -H "Authorization: Bearer <token>" \
  "https://localhost:8080/api/contexts/my-context/entries/export" -o my-context-export.json

Example response body:

{
  "version": 1,
  "context": "my-context",
  "count": 2,
  "entries": [
    {
      "tokenHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "replacement": "David Jones",
      "filterType": "PERSON",
      "replacementUuid": false
    },
    {
      "tokenHash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
      "replacement": "842-436-2042",
      "filterType": "PHONE_NUMBER",
      "replacementUuid": false
    }
  ]
}

Import a Mapping Table into a Context

Method Endpoint Description
POST /api/contexts/{name}/entries/import Import token-to-replacement mappings into a context.

Accepts a document in the same format produced by the export endpoint and inserts its mappings into the named context. The context must already exist (create it first with POST /api/contexts).

Authorization: only the user that created the context or an admin may import into it. Any other caller receives 404 Not Found.

Query Parameters

  • on_conflict (optional, default: skip) - What to do when an incoming token already exists in the target context:
    • skip - leave the existing replacement unchanged (preserves replacements already learned in this context).
    • overwrite - replace the existing replacement with the imported one.
  • owner (optional) - The username of the context's owner. Because context names are unique only per user, an admin uses this to identify another user's context unambiguously. When omitted, the import applies to the caller's own context with the given name. Supplying an owner other than yourself requires admin privileges and cross-user access being enabled (ADMIN_CROSS_USER_ACCESS_ENABLED=true; disabled by default); otherwise it receives 404 Not Found.

Behavior and Validation

  • The payload is fully validated before anything is written, so a malformed entry cannot leave a partially-imported table. Each entry must have a valid 64-character hex tokenHash and a non-empty replacement.
  • The export must come from a context owned by the same username, in a deployment sharing this one's PHILTER_ENCRYPTION_KEY. Token hashes are keyed per user (see Export), and a hash from another user or a differently-keyed deployment is indistinguishable from a valid one: it will import successfully and then never match during redaction.
  • Imported entries start with a read count of zero. As with normal redaction, imports honor the context's MAX_CONTEXT_SIZE and may evict least-read entries when the context is full.

Returns 200 OK with a summary, 400 Bad Request for an invalid on_conflict value or malformed payload, and 404 Not Found if the context does not exist or the caller is not authorized to access it.

Example request:

curl -X POST -k -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  --data-binary @my-context-export.json \
  "https://localhost:8080/api/contexts/my-context/entries/import?on_conflict=skip"

Example response:

{
  "total": 2,
  "inserted": 2,
  "overwritten": 0,
  "skipped": 0
}

Capacity

Each user can have at most 10 contexts, including the default context every user starts with. The limit counts only the user's own contexts, and it is fixed: no setting changes it. Creating an eleventh returns 409 Conflict with reason set to context_limit_reached; delete a context to make room. A name you already use is reported as context_exists whether or not you are at the limit. An administrator creating a context with owner is held to the other user's limit.

Each context is bounded by MAX_CONTEXT_SIZE (default 10,000 entries; overridable via the MAX_CONTEXT_SIZE environment variable). When the limit is reached, the least-read entry is evicted before a new one is inserted (ties broken by oldest). Disambiguation vector storage is similarly bounded by MAX_VECTORS_PER_CONTEXT (default 100,000, FIFO eviction).

Deleting /api/contexts/{name}/entries/{entryId} requires both the named context and entry ID to match within the target owner. Using an entry ID from a different context returns 404 and leaves the mapping intact. JSON resource responses use Content-Type: application/json.