# Error Codes
Source: https://docs.ambientmeta.com/api-reference/errors
All error codes returned by the AmbientMeta API
## Error Response Format
All errors return a consistent format with actionable suggestions.
```json theme={null}
{
"error": {
"code": "error_code",
"message": "Human-readable description",
"suggestion": "How to fix it"
}
}
```
## Error Reference
| Code | HTTP | Description | Suggestion |
| ----------------------- | ---- | ------------------------------------ | ------------------------------------------- |
| `invalid_api_key` | 401 | API key missing or invalid | Check your API key in the dashboard |
| `unauthorized` | 401 | Could not resolve organization | Verify your API key is valid |
| `rate_limited` | 429 | Too many requests | Wait and retry, or upgrade your plan |
| `extension_daily_limit` | 429 | Chrome extension daily limit reached | Upgrade to Pro for unlimited use |
| `session_not_found` | 404 | Session ID doesn't exist | Check the session\_id from sanitize |
| `session_expired` | 410 | Session has expired | Sessions last 24 hours, re-sanitize |
| `empty_text` | 400 | Text field is empty | Provide text to sanitize |
| `text_too_large` | 413 | Text exceeds 100KB | Split into smaller chunks |
| `invalid_request` | 400 | Malformed request body | See API docs for the correct request format |
| `invalid_pattern` | 400 | Regex pattern is invalid | Check your pattern syntax |
| `pattern_not_found` | 404 | Pattern ID doesn't exist | List patterns to find valid IDs |
| `insight_not_found` | 404 | Insight ID doesn't exist | List insights to find valid IDs |
| `internal_error` | 500 | An unexpected error occurred | Try again or contact support |
## Rate Limits
| Plan | Requests/minute |
| -------- | --------------- |
| Free | 20 |
| Pro | 600 |
| Pro Plus | 2,000 |
## Rate Limit Headers
| Header | Description |
| ----------------------- | -------------------------------- |
| `X-RateLimit-Limit` | Your rate limit |
| `X-RateLimit-Remaining` | Requests remaining |
| `X-RateLimit-Reset` | Unix timestamp when limit resets |
## Handling Errors in Python
```python theme={null}
from ambientmeta import AmbientMeta
from ambientmeta.exceptions import RateLimitError, NotFoundError
client = AmbientMeta(api_key="am_live_xxx")
try:
result = client.sanitize(text)
except RateLimitError as e:
print(f"Rate limited: {e.message}")
except NotFoundError:
print("Session not found or expired, re-sanitize")
```
# POST /v1/feedback
Source: https://docs.ambientmeta.com/api-reference/feedback
Submit corrections to improve detection accuracy
Every correction you submit helps the system learn. Corrections drive contradiction detection, rule formulation, and accuracy improvements specific to your data.
## Authentication
```
X-API-Key: am_live_your_key_here
```
## Batch Format (Preferred)
Submit multiple corrections in a single request. Each correction references either a placeholder from the sanitize response or raw text for missed entities.
| Field | Type | Required | Description |
| ----------------------------- | ------ | ----------- | --------------------------------------------------------------------------------------------- |
| `session_id` | string | Yes | Session ID from the sanitize response |
| `corrections` | array | Yes | Array of correction objects (max 50) |
| `corrections[].placeholder` | string | Conditional | Reference to `[TYPE_N]` placeholder. Required for `misclassification` and `false_positive` |
| `corrections[].span_text` | string | Conditional | Raw text span. Required for `missed_entity` |
| `corrections[].correct_type` | string | Conditional | What the entity should be classified as. Required for `misclassification` and `missed_entity` |
| `corrections[].feedback_type` | string | Yes | `misclassification`, `false_positive`, `missed_entity`, or `wrong_type` |
### Example Request
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/feedback \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"session_id": "ses_a1b2c3d4e5f6",
"corrections": [
{"placeholder": "[PHONE_NUMBER_1]", "correct_type": "NPI", "feedback_type": "misclassification"},
{"span_text": "Dr. Jane Smith", "correct_type": "PERSON", "feedback_type": "missed_entity"},
{"placeholder": "[SSN_1]", "feedback_type": "false_positive"}
]
}'
```
### Example Response
```json theme={null}
{
"accepted": 3,
"correction_ids": ["cor_xxx", "cor_yyy", "cor_zzz"],
"contradictions_detected": 1,
"message": "Thank you. 1 contradiction detected — check /v1/insights for details.",
"status": "recorded"
}
```
## Response Fields
| Field | Type | Description |
| ------------------------- | ------- | ----------------------------------------------------------- |
| `accepted` | integer | Number of corrections accepted |
| `correction_ids` | array | IDs assigned to each correction |
| `contradictions_detected` | integer | Number of contradictions found against previous corrections |
| `message` | string | Human-readable summary |
| `status` | string | Always `"recorded"` |
**Contradiction detection is automatic.** When you correct an entity in one direction (e.g., PHONE\_NUMBER to NPI) and a previous correction went the other way (NPI to PHONE\_NUMBER), the system detects this as a contradiction and creates an insight. Check [GET /v1/insights](/api-reference/insights) for details and resolution options.
## Feedback Types
| Type | When to Use | Required Fields |
| ------------------- | ---------------------------------------------------- | ------------------------------ |
| `misclassification` | Entity was detected but classified as the wrong type | `placeholder` + `correct_type` |
| `wrong_type` | Alias for `misclassification` | `placeholder` + `correct_type` |
| `missed_entity` | System failed to detect an entity | `span_text` + `correct_type` |
| `false_positive` | System flagged something that is not PII | `placeholder` |
## Legacy Single-Correction Format
The original single-correction format is still supported for backwards compatibility.
| Field | Type | Required | Description |
| --------------- | ------ | -------- | ----------------------------------------------------------------------- |
| `session_id` | string | Yes | Session ID from the sanitize response |
| `feedback_type` | string | Yes | `wrong_type`, `missed_entity`, `false_positive`, or `misclassification` |
| `text_snippet` | string | Yes | The text span being corrected |
| `expected_type` | string | No | What the entity should be classified as |
### Legacy Example
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/feedback \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"session_id": "ses_a1b2c3d4e5f6",
"feedback_type": "wrong_type",
"text_snippet": "1234567890",
"expected_type": "NPI"
}'
```
## SDK Example
```python theme={null}
from ambientmeta import AmbientMeta
client = AmbientMeta(api_key="am_live_xxx")
# Sanitize first
result = client.sanitize("Contact NPI 1234567890 at 555-867-5309")
# Chain corrections on the result
result.correct("[PHONE_NUMBER_1]", "NPI") # Misclassified
result.report_missed("Dr. Smith", "PERSON") # Missed entity
result.report_false_positive("[LOCATION_1]") # False positive
# Submit all corrections at once
feedback = result.submit_corrections()
print(f"Accepted: {feedback.accepted}, Contradictions: {feedback.contradictions_detected}")
```
## Errors
| Code | HTTP Status | Description |
| ------------------- | ----------- | --------------------------------------- |
| `invalid_api_key` | 401 | API key is missing or invalid |
| `session_not_found` | 404 | Session ID doesn't exist or has expired |
| `rate_limited` | 429 | Too many requests |
[View all error codes](/api-reference/errors)
# GET /v1/health
Source: https://docs.ambientmeta.com/api-reference/health
Check API availability and version
**No authentication required.** This endpoint is public and can be used for uptime monitoring.
## Example Request
```bash theme={null}
curl https://api.ambientmeta.com/v1/health
```
## Response
| Field | Type | Description |
| --------- | ------ | -------------------------------------------- |
| `status` | string | Always `"healthy"` when the API is reachable |
| `version` | string | Current API version |
### Example Response
```json theme={null}
{
"status": "healthy",
"version": "1.1.0"
}
```
# Insights
Source: https://docs.ambientmeta.com/api-reference/insights
View detected contradictions, proposed improvements, and knowledge metrics
As you submit corrections via [POST /v1/feedback](/api-reference/feedback), the system analyzes patterns in your data. Insights surface contradictions, suggest disambiguation rules, and track your overall detection accuracy.
## GET /v1/insights
### Example Request
```bash theme={null}
curl https://api.ambientmeta.com/v1/insights \
-H "X-API-Key: am_live_xxx"
```
### Example Response
```json theme={null}
{
"pending_conflicts": [
{
"id": "ins_abc123",
"type": "contradiction_cluster",
"title": "PHONE_NUMBER vs NPI disambiguation needed",
"description": "The system detected that PHONE_NUMBER and NPI are sometimes confused in your data.",
"status": "pending",
"data": {},
"created_at": "2026-02-13T12:00:00Z"
}
],
"proposed_improvements": [],
"discovered_patterns": [],
"knowledge_depth": {
"ckd_score": 0.42,
"maturity": "developing",
"strongest_entity": {"type": "EMAIL_ADDRESS", "confidence": 0.99},
"weakest_entity": {"type": "NPI", "confidence": 0.71},
"contradictions_pending": 3,
"contradictions_resolved": 12,
"golden_set_size": 234,
"compiled_rules": 18,
"on_demand_hit_rate": 0.45
}
}
```
### Response Fields
| Field | Type | Description |
| ----------------------- | ------ | ----------------------------------------------- |
| `pending_conflicts` | array | Contradictions that need resolution |
| `proposed_improvements` | array | System-generated rule suggestions |
| `discovered_patterns` | array | Recurring formats detected from missed entities |
| `knowledge_depth` | object | Your Customer Knowledge Depth metrics |
### Insight Item Fields
| Field | Type | Description |
| ------------- | ------ | ---------------------------------------------------------------------------------------- |
| `id` | string | Insight identifier |
| `type` | string | `contradiction_cluster`, `pattern_overlap`, `context_condition`, or `discovered_pattern` |
| `title` | string | Human-readable summary |
| `description` | string | Detailed explanation |
| `status` | string | `pending`, `approved`, `rejected`, or `refined` |
| `data` | object | Insight-specific metadata |
| `created_at` | string | ISO 8601 timestamp |
### Knowledge Depth Fields
| Field | Type | Description |
| ------------------------- | ------- | ---------------------------------------------------- |
| `ckd_score` | float | Customer Knowledge Depth score (0.0 to 1.0) |
| `maturity` | string | `nascent`, `developing`, `established`, or `expert` |
| `strongest_entity` | object | Entity type with highest detection confidence |
| `weakest_entity` | object | Entity type with lowest detection confidence |
| `contradictions_pending` | integer | Unresolved contradictions |
| `contradictions_resolved` | integer | Resolved contradictions |
| `golden_set_size` | integer | Number of validated examples in your golden set |
| `compiled_rules` | integer | Active disambiguation rules |
| `on_demand_hit_rate` | float | Fraction of detections where a compiled rule applied |
**Maturity levels:** nascent (0.0-0.2), developing (0.2-0.5), established (0.5-0.8), expert (0.8-1.0). Higher scores mean better detection accuracy tailored to your data.
## POST /v1/insights/\{id}/resolve
Resolve a pending insight by approving, refining, or rejecting the proposed rule.
| Field | Type | Required | Description |
| --------------- | ------ | ------------ | ------------------------------------------- |
| `resolution` | string | Yes | `approve`, `refine`, or `reject` |
| `modifications` | object | For `refine` | Modifications to apply to the proposed rule |
| `reason` | string | For `reject` | Reason for rejection |
### Approve
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/insights/ins_abc123/resolve \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"resolution": "approve"}'
```
### Refine
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/insights/ins_abc123/resolve \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"resolution": "refine",
"modifications": {
"context_condition": {
"keywords": ["prescriber", "NPI", "provider", "attending", "clinic"],
"distance": 150
}
}
}'
```
### Reject
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/insights/ins_abc123/resolve \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"resolution": "reject", "reason": "These are all phone numbers in our system"}'
```
### Response
```json theme={null}
{
"status": "approved",
"golden_set_entries_created": 2
}
```
| Field | Type | Description |
| ---------------------------- | ------- | ------------------------------------------------------------- |
| `status` | string | Resolution status (`approved`, `refined`, or `rejected`) |
| `golden_set_entries_created` | integer | Number of new golden set entries created from this resolution |
**All resolutions generate training data.** Approving and refining create positive examples. Rejecting creates a negative signal. Every interaction improves accuracy.
## Errors
| Code | HTTP Status | Description |
| ------------------- | ----------- | ----------------------------- |
| `invalid_api_key` | 401 | API key is missing or invalid |
| `insight_not_found` | 404 | Insight ID doesn't exist |
| `rate_limited` | 429 | Too many requests |
[View all error codes](/api-reference/errors)
# POST /v1/patterns
Source: https://docs.ambientmeta.com/api-reference/patterns
Create custom entity patterns for your organization
## Why Custom Patterns?
Standard entities cover names, emails, etc. Custom patterns let you detect org-specific data like employee IDs (`EMP-123456`), project codes, internal account numbers, or any custom identifier.
## Request Body
| Field | Type | Required | Description |
| ------------- | ------ | -------- | ----------------------------------- |
| `name` | string | Yes | Pattern name (e.g., "EMPLOYEE\_ID") |
| `pattern` | string | Yes | Regex pattern to match |
| `description` | string | No | Human-readable description |
| `examples` | array | No | Example matches for validation |
### Example Request
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/patterns \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "EMPLOYEE_ID",
"pattern": "EMP-[0-9]{6}",
"description": "Internal employee identifier",
"examples": ["EMP-123456", "EMP-789012"]
}'
```
## Response
| Field | Type | Description |
| ------------ | ------------ | ----------------------------------------- |
| `pattern_id` | string | Unique pattern identifier |
| `name` | string | Pattern name |
| `status` | string | Pattern status (`"active"`) |
| `analysis` | object\|null | Overlap and quality analysis (see below) |
| `conflict` | object\|null | Conflict details if overlaps are detected |
### Example Response
```json theme={null}
{
"pattern_id": "pat_xyz789",
"name": "EMPLOYEE_ID",
"status": "active",
"analysis": {
"overlaps_with": [],
"overlap_count": 0,
"contradiction_risk": "low",
"golden_set_coverage": 0,
"suggested_improvements": []
},
"conflict": null
}
```
### Response with Conflict
If the new pattern overlaps with existing entity detections, the response includes a `conflict` block:
```json theme={null}
{
"pattern_id": "pat_abc123",
"name": "ACCOUNT_NUMBER",
"status": "active",
"analysis": {
"overlaps_with": ["PHONE_NUMBER"],
"overlap_count": 23,
"contradiction_risk": "medium",
"golden_set_coverage": 0,
"suggested_improvements": []
},
"conflict": {
"has_conflict": true,
"conflicting_spans": 23,
"conflicting_type": "PHONE_NUMBER",
"resolution_required": true,
"resolution_options": [
{"option": "reclassify_all", "description": "Reclassify all 23 spans as ACCOUNT_NUMBER"},
{"option": "add_context_condition", "description": "Add context rule to distinguish"},
{"option": "refine_pattern", "description": "Narrow the pattern to avoid overlap"}
],
"insight_id": "ins_abc"
}
}
```
When a conflict is detected, an insight is automatically created. Use the [Insights API](/api-reference/insights) to resolve it.
## Using Custom Patterns
Once created, custom patterns are automatically included in sanitization when `config.custom_patterns` is set to `true` in the request body:
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "Contact EMP-123456 about the project",
"config": {"custom_patterns": true}
}'
```
Response:
```json theme={null}
{
"sanitized": "Contact [EMPLOYEE_ID_1] about the project",
"session_id": "ses_...",
"entities_found": 1
}
```
## List Patterns
`GET /v1/patterns` returns all patterns for your organization.
## Delete Patterns
`DELETE /v1/patterns/{pattern_id}` removes a pattern.
# POST /v1/rehydrate
Source: https://docs.ambientmeta.com/api-reference/rehydrate
Restore original PII to a sanitized response
## Authentication
```
X-API-Key: am_live_your_key_here
```
## Request Body
| Field | Type | Required | Description |
| ------------ | ------ | -------- | --------------------------------------- |
| `text` | string | Yes | Text containing placeholders to restore |
| `session_id` | string | Yes | Session ID from the sanitize response |
### Example Request
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/rehydrate \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "I will contact [PERSON_1] at [EMAIL_ADDRESS_1] tomorrow.", "session_id": "ses_a1b2c3d4e5f6"}'
```
## Response
| Field | Type | Description |
| ------------------- | ------- | ------------------------------------ |
| `text` | string | Text with original entities restored |
| `entities_restored` | integer | Number of placeholders replaced |
| `processing_ms` | float | Processing time in milliseconds |
### Example Response
```json theme={null}
{
"text": "I will contact John Smith at john@acme.com tomorrow.",
"entities_restored": 2,
"processing_ms": 3
}
```
## Session Expiry
**Sessions expire after 24 hours.** If you call rehydrate with an expired session, you'll receive a `session_expired` error. Re-sanitize the original text to create a new session.
## Errors
| Code | HTTP Status | Description |
| ------------------- | ----------- | ----------------------------------- |
| `session_not_found` | 404 | Session ID doesn't exist |
| `session_expired` | 410 | Session has expired (24 hour limit) |
| `invalid_api_key` | 401 | API key is missing or invalid |
[View all error codes](/api-reference/errors)
# POST /v1/sanitize
Source: https://docs.ambientmeta.com/api-reference/sanitize
Strip PII from text before sending to external LLMs
## Authentication
```
X-API-Key: am_live_your_key_here
```
## Request Body
| Field | Type | Required | Description |
| ----------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `text` | string | Yes | Text to sanitize (max 100KB) |
| `mode` | string | No | `"sanitize"` (default) or `"redact"`. Redact mode permanently removes PII and returns `session_id: null`. |
| `config.entities` | array | No | Entity types to detect (default: all). See entity types below. |
| `config.confidence_threshold` | float | No | Minimum confidence score to include a detection (default: 0.0) |
| `config.custom_patterns` | boolean | No | Include org's custom patterns (default: false) |
| `config.storage_overrides` | object | No | Per-entity storage tier overrides (e.g., `{"SSN": 1}` for never-store) |
### Example Request
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "Email John Smith at john@acme.com about the NYC merger",
"config": {"entities": ["PERSON", "EMAIL_ADDRESS", "LOCATION"]}
}'
```
## Response
| Field | Type | Description |
| ---------------- | ------------ | ------------------------------------------------------------------------ |
| `sanitized` | string | Text with PII replaced by placeholders |
| `session_id` | string\|null | ID for rehydration (valid 24 hours). `null` when `mode="redact"`. |
| `redacted` | boolean | `true` if redact mode was used |
| `entities_found` | integer | Number of entities detected |
| `entities` | array | Details of each detected entity (see below) |
| `metadata` | object | Processing metadata including `format_type` and `disambiguation_applied` |
| `processing_ms` | float | Processing time in milliseconds |
### Entity Object
| Field | Type | Description |
| ------------- | ------------ | -------------------------------------------------- |
| `placeholder` | string | The replacement token, e.g. `[PERSON_1]` |
| `type` | string | Entity type (see Entity Types below) |
| `confidence` | float | Detection confidence score (0.0 to 1.0) |
| `start` | integer | Start character offset in the original text |
| `end` | integer | End character offset in the original text |
| `structure` | object\|null | Structural context: `region_type`, `label`, `line` |
### Example Response
```json theme={null}
{
"sanitized": "Email [PERSON_1] at [EMAIL_ADDRESS_1] about the [LOCATION_1] merger",
"session_id": "ses_a1b2c3d4e5f6",
"redacted": false,
"entities_found": 3,
"entities": [
{
"placeholder": "[PERSON_1]",
"type": "PERSON",
"confidence": 0.97,
"start": 6,
"end": 16,
"structure": null
},
{
"placeholder": "[EMAIL_ADDRESS_1]",
"type": "EMAIL_ADDRESS",
"confidence": 0.99,
"start": 20,
"end": 33,
"structure": null
},
{
"placeholder": "[LOCATION_1]",
"type": "LOCATION",
"confidence": 0.92,
"start": 44,
"end": 47,
"structure": null
}
],
"metadata": {
"format_type": "plain_prose",
"disambiguation_applied": false
},
"processing_ms": 14.2
}
```
## Redact Mode
Set `mode: "redact"` to permanently remove PII. Redacted responses cannot be rehydrated.
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "Email john@acme.com", "mode": "redact"}'
```
Response will have `"session_id": null` and `"redacted": true`.
## Entity Types
| Type | Description | Detection | Examples |
| --------------- | ---------------------------- | ----------------------- | ------------------------------------- |
| `PERSON` | Names of people | NER + patterns | John Smith, Dr. Jane Doe |
| `EMAIL_ADDRESS` | Email addresses | Regex + validation | [john@acme.com](mailto:john@acme.com) |
| `PHONE_NUMBER` | Phone numbers | Multi-format regex | (555) 123-4567 |
| `SSN` | Social Security Numbers | Regex + checksum | 123-45-6789 |
| `CREDIT_CARD` | Credit card numbers | Regex + Luhn | 4532-1234-5678-9012 |
| `LOCATION` | Places, cities | NER | NYC, San Francisco |
| `ADDRESS` | Physical addresses | Regex + NER | 123 Main St, Suite 200 |
| `NPI` | National Provider Identifier | Regex + Luhn (10-digit) | 1234567890 |
| `DEA_NUMBER` | DEA Registration Number | Regex + checksum | AB1234567 |
| `MRN` | Medical Record Number | Context-aware regex | MRN-12345678 |
`EMAIL` and `PHONE` are accepted as aliases for `EMAIL_ADDRESS` and `PHONE_NUMBER` in the `config.entities` array.
## Errors
| Code | HTTP Status | Description |
| ----------------- | ----------- | ----------------------------- |
| `invalid_api_key` | 401 | API key is missing or invalid |
| `empty_text` | 400 | Text field is empty |
| `text_too_large` | 413 | Text exceeds 100KB limit |
| `rate_limited` | 429 | Too many requests |
[View all error codes](/api-reference/errors)
# Chrome Extension
Source: https://docs.ambientmeta.com/guides/chrome-extension
Detect and strip PII directly in ChatGPT, Claude, and Gemini
## Overview
**AmbientMeta Privacy Guard** is a Chrome extension that automatically detects PII as you type into LLM chat interfaces. It works on:
* [ChatGPT](https://chatgpt.com)
* [Claude](https://claude.ai)
* [Gemini](https://gemini.google.com)
The extension highlights detected entities inline and lets you sanitize text with one click before sending it to the LLM.
## How It Works
1. **Real-time scanning** — As you type in a chat input, the extension calls the AmbientMeta API to detect PII entities in your text.
2. **Inline highlighting** — Detected entities (names, emails, phone numbers, etc.) are highlighted directly in the input field.
3. **One-click sanitize** — Click the sanitize button to replace all detected PII with safe placeholders before sending your message.
All detection runs through the AmbientMeta backend API (`POST /v1/sanitize`), so detection quality is identical to the API and every scan feeds the learning flywheel.
## Authentication
The extension supports two modes:
### Signed Out (Free)
* Uses a shared extension key
* Limited to **50 sanitizations per day** (detection/highlighting is unlimited)
* No account required
### Signed In (Google)
* Sign in with Google via the extension popup
* The extension authenticates using `POST /v1/auth/google-extension`
* An API key is automatically provisioned for your account
* Unlimited sanitizations on paid plans
## Daily Limits
| Mode | Detection (highlighting) | Sanitization |
| --------------------- | ------------------------ | ------------------------ |
| Signed out | Unlimited | 50/day |
| Signed in (free plan) | Unlimited | 50/day (server-enforced) |
| Signed in (Pro) | Unlimited | Unlimited |
| Signed in (Pro Plus) | Unlimited | Unlimited |
Detection calls (for inline highlighting) do **not** count toward the daily limit. Only full sanitization requests are counted.
## Configuration
The extension scans for these entity types by default:
* `PERSON` — Names
* `EMAIL_ADDRESS` — Email addresses
* `PHONE_NUMBER` — Phone numbers
* `SSN` — Social Security Numbers
* `CREDIT_CARD` — Credit card numbers
* `LOCATION` — Places and cities
* `ADDRESS` — Physical addresses
## Technical Details
* **Manifest V3** Chrome extension
* Detection debounced at 1.5 seconds after typing stops
* Minimum 10 characters before scanning triggers
* All API calls go through the background service worker
* Credentials stored in `chrome.storage.local`
## Installation
The extension is available from the Chrome Web Store. After installing:
1. Click the AmbientMeta icon in your toolbar
2. (Optional) Sign in with Google for higher limits
3. Navigate to ChatGPT, Claude, or Gemini
4. Start typing — PII will be highlighted automatically
# Working with Document Formats
Source: https://docs.ambientmeta.com/guides/document-formats
Extract plain text from PDFs, DOCX, RTF, and HTML before sending to the API
The AmbientMeta API accepts **plain text only**. If your source data lives in PDF, DOCX, RTF, or HTML files, you need to extract the text before calling `/v1/sanitize`.
**Common pitfall:** Sending raw file bytes (e.g., the binary contents of an RTF or PDF) directly as the `text` field will not work. The API will attempt to detect PII in the raw markup or binary data, producing unreliable results — missed entities, false positives on control sequences, or garbled output.
## Why plain text?
AmbientMeta's detection engine analyzes the **structural layout** of human-readable text — prose, key-value pairs, tables, and lists. Binary formats like PDF and DOCX contain rendering instructions, embedded fonts, and metadata that interfere with detection. RTF and HTML contain markup tags that break entity boundary detection.
Always convert to plain text first, then sanitize.
## Python
### PDF
```python theme={null}
import pymupdf # pip install pymupdf
from ambientmeta import AmbientMeta
client = AmbientMeta(api_key="am_live_xxx")
doc = pymupdf.open("patient_record.pdf")
text = "\n".join(page.get_text() for page in doc)
result = client.sanitize(text)
print(result.sanitized)
```
### DOCX
```python theme={null}
import docx # pip install python-docx
document = docx.Document("intake_form.docx")
text = "\n".join(p.text for p in document.paragraphs)
result = client.sanitize(text)
```
### RTF
```python theme={null}
from striprtf.striprtf import rtf_to_text # pip install striprtf
with open("referral_letter.rtf", "r") as f:
text = rtf_to_text(f.read())
result = client.sanitize(text)
```
### HTML
```python theme={null}
from bs4 import BeautifulSoup # pip install beautifulsoup4
with open("report.html", "r") as f:
soup = BeautifulSoup(f.read(), "html.parser")
text = soup.get_text(separator="\n", strip=True)
result = client.sanitize(text)
```
## Shell
Use common CLI tools to extract text, then pipe to the API with `curl`.
### PDF (pdftotext)
```bash theme={null}
# apt-get install poppler-utils (Debian/Ubuntu)
# brew install poppler (macOS)
pdftotext patient_record.pdf - | \
jq -Rs '{"text": .}' | \
curl -s -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d @-
```
### DOCX (pandoc)
```bash theme={null}
# apt-get install pandoc (Debian/Ubuntu)
# brew install pandoc (macOS)
pandoc intake_form.docx -t plain | \
jq -Rs '{"text": .}' | \
curl -s -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d @-
```
### RTF (unrtf)
```bash theme={null}
# apt-get install unrtf
unrtf --text referral_letter.rtf | \
jq -Rs '{"text": .}' | \
curl -s -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d @-
```
## Node.js
### PDF
```javascript theme={null}
import { readFileSync } from "fs";
import pdf from "pdf-parse"; // npm install pdf-parse
const buffer = readFileSync("patient_record.pdf");
const { text } = await pdf(buffer);
const res = await fetch("https://api.ambientmeta.com/v1/sanitize", {
method: "POST",
headers: {
"X-API-Key": "am_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
const result = await res.json();
console.log(result.sanitized);
```
### DOCX
```javascript theme={null}
import mammoth from "mammoth"; // npm install mammoth
const { value: text } = await mammoth.extractRawText({
path: "intake_form.docx",
});
const res = await fetch("https://api.ambientmeta.com/v1/sanitize", {
method: "POST",
headers: {
"X-API-Key": "am_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
```
### HTML
```javascript theme={null}
import { JSDOM } from "jsdom"; // npm install jsdom
import { readFileSync } from "fs";
const html = readFileSync("report.html", "utf-8");
const text = new JSDOM(html).window.document.body.textContent;
const res = await fetch("https://api.ambientmeta.com/v1/sanitize", {
method: "POST",
headers: {
"X-API-Key": "am_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ text }),
});
```
## Tips
**Preserve structure where possible.** The API's detection engine understands key-value pairs, tables, and lists. When extracting text, prefer tools that maintain line breaks and spacing (e.g., `pdftotext -layout`) over those that collapse everything into a single paragraph.
**Large documents:** The `text` field has a 100KB limit. For documents that exceed this, split the extracted text into chunks and sanitize each chunk separately. Each call returns its own `session_id` for rehydration.
## Quick reference
| Format | Python | Shell | Node.js |
| ------ | ---------------- | ----------- | ----------- |
| PDF | `pymupdf` | `pdftotext` | `pdf-parse` |
| DOCX | `python-docx` | `pandoc` | `mammoth` |
| RTF | `striprtf` | `unrtf` | — |
| HTML | `beautifulsoup4` | `pandoc` | `jsdom` |
# LangChain Integration
Source: https://docs.ambientmeta.com/guides/langchain
Safely use LangChain with any LLM without exposing PII
## Install
```bash theme={null}
pip install langchain-ambientmeta
```
## Quick Start
```python theme={null}
from langchain_ambientmeta import PrivacyLLM
from langchain_openai import ChatOpenAI
# Wrap your LLM with privacy protection
safe_llm = PrivacyLLM(
llm=ChatOpenAI(model="gpt-4"),
api_key="am_live_xxx",
)
# Use normally — PII is automatically handled
response = safe_llm.invoke("Summarize John Smith's file at john@acme.com")
# OpenAI never sees the real PII
```
**That's it!** The wrapper automatically sanitizes input, calls the LLM with safe text, and rehydrates the response.
## How It Works
1. Your input is sanitized before reaching the LLM
2. The LLM processes the sanitized text
3. The response is rehydrated with original entities
4. You get back the complete response
## With Chains (LCEL)
```python theme={null}
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("Answer this question: {query}")
chain = prompt | safe_llm
result = chain.invoke({"query": "What is John Smith's email?"})
```
## With RAG
```python theme={null}
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template(
"Answer the question based on context:\n{context}\n\nQuestion: {input}"
)
combine_chain = create_stuff_documents_chain(safe_llm, prompt)
rag_chain = create_retrieval_chain(your_retriever, combine_chain)
result = rag_chain.invoke({"input": "Find information about employee EMP-123456"})
```
## Configuration
```python theme={null}
safe_llm = PrivacyLLM(
llm=ChatOpenAI(model="gpt-4"),
api_key="am_live_xxx",
entities=["PERSON", "EMAIL_ADDRESS", "SSN"], # Optional: specific entities only
auto_rehydrate=True, # Automatically restore PII in responses (default: True)
)
```
# LlamaIndex Integration
Source: https://docs.ambientmeta.com/guides/llamaindex
Build compliant RAG pipelines without exposing PII
## Install
```bash theme={null}
pip install llamaindex-ambientmeta
```
## Quick Start
```python theme={null}
from llama_index.core import VectorStoreIndex, Settings
from llama_index.llms.openai import OpenAI
from llamaindex_ambientmeta import PrivacyLLM
# Wrap your LLM with privacy protection
Settings.llm = PrivacyLLM(
llm=OpenAI(),
api_key="am_live_xxx",
)
# Build your index as normal
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
# Queries are automatically sanitized
response = query_engine.query("What's in John Smith's contract?")
# LLM never sees real names
```
## How It Works
1. Your query is sanitized before processing
2. RAG retrieval happens with sanitized text
3. The LLM generates a response with placeholders
4. Response is rehydrated with original entities
## With Chat Engine
```python theme={null}
chat_engine = index.as_chat_engine()
# Multi-turn conversations stay private
response = chat_engine.chat("Tell me about employee EMP-123456")
response = chat_engine.chat("What's their email?")
```
## Configuration
```python theme={null}
Settings.llm = PrivacyLLM(
llm=OpenAI(model="gpt-4"),
api_key="am_live_xxx",
entities=["PERSON", "EMAIL_ADDRESS", "SSN"], # Optional: detect specific entities only
auto_rehydrate=True, # Automatically restore PII in responses (default: True)
)
```
## With Any LLM
Works with any LlamaIndex-supported LLM:
```python theme={null}
from llama_index.llms.anthropic import Anthropic
Settings.llm = PrivacyLLM(
llm=Anthropic(model="claude-sonnet-4-20250514"),
api_key="am_live_xxx",
)
```
# Introduction
Source: https://docs.ambientmeta.com/introduction
The privacy layer for AI — protect PII in every LLM call
# AmbientMeta Documentation
Use any AI without exposing sensitive data. Strip PII before the LLM, restore it after.
Get your first API call working.
Complete endpoint documentation.
Install and use the official SDK.
LangChain, LlamaIndex, and more.
Submit corrections to improve detection.
Resolve conflicts and improve accuracy.
## How It Works
AmbientMeta Privacy Gateway sits between your application and external LLMs:
1. **Sanitize** — Send text to our API. We detect and replace PII with placeholders like `[PERSON_1]`.
2. **Call your LLM** — Send the sanitized text to Claude, GPT-4, or any model. The LLM never sees real PII.
3. **Rehydrate** — Send the LLM's response back to us. We restore the original entities.
```python theme={null}
from ambientmeta import AmbientMeta
import openai
client = AmbientMeta(api_key="am_live_xxx")
# 1. Sanitize user input
result = client.sanitize("Email john@acme.com about the project")
# 2. Call your LLM (with safe text)
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": result.sanitized}]
)
# 3. Rehydrate the response
final = client.rehydrate(response.choices[0].message.content, result.session_id)
print(final.text) # original PII restored
```
## Supported Entity Types
| Entity | Examples | Detection |
| --------------- | ------------------------------------- | ----------------------- |
| `PERSON` | John Smith, Dr. Jane Doe | NER + patterns |
| `EMAIL_ADDRESS` | [john@acme.com](mailto:john@acme.com) | Regex + validation |
| `PHONE_NUMBER` | (555) 123-4567 | Multi-format regex |
| `SSN` | 123-45-6789 | Regex + checksum |
| `CREDIT_CARD` | 4532-1234-5678-9012 | Regex + Luhn |
| `LOCATION` | NYC, San Francisco | NER |
| `ADDRESS` | 123 Main St, Suite 200 | Regex + NER |
| `NPI` | 1234567890 | Regex + Luhn (10-digit) |
| `DEA_NUMBER` | AB1234567 | Regex + checksum |
| `MRN` | MRN-12345678 | Context-aware regex |
## Base URL
```
https://api.ambientmeta.com/v1
```
## Authentication
Include your API key in the `X-API-Key` header:
```
X-API-Key: am_live_your_key_here
```
**Ready to start?** Follow the [Quickstart guide](/quickstart) to make your first API call.
# Quickstart
Source: https://docs.ambientmeta.com/quickstart
Get your first API call working
## 1. Get your API key
Sign up at [ambientmeta.com](https://ambientmeta.com) and copy your API key from the dashboard.
## 2. Install the SDK
```bash theme={null}
pip install ambientmeta
```
## 3. Sanitize some text
```python theme={null}
from ambientmeta import AmbientMeta
client = AmbientMeta(api_key="am_live_your_key_here")
# Sanitize text before sending to an LLM
result = client.sanitize("Email John Smith at john@acme.com about the project")
print(result.sanitized)
# "Email [PERSON_1] at [EMAIL_ADDRESS_1] about the project"
print(result.session_id)
# "ses_abc123..."
```
## 4. Call your LLM (with safe text)
```python theme={null}
import openai
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": result.sanitized}]
)
llm_response = response.choices[0].message.content
```
## 5. Rehydrate the response
```python theme={null}
final = client.rehydrate(llm_response, result.session_id)
print(final.text)
# Original names and emails restored
```
**That's it!** Your LLM never saw the real PII. The original data stayed in your control the entire time.
## Using curl
### Sanitize
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/sanitize \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "Email John Smith at john@acme.com about the merger"}'
```
Response:
```json theme={null}
{
"sanitized": "Email [PERSON_1] at [EMAIL_ADDRESS_1] about the merger",
"session_id": "ses_a1b2c3d4e5f6",
"redacted": false,
"entities_found": 2,
"entities": [
{"placeholder": "[PERSON_1]", "type": "PERSON", "confidence": 0.97, "start": 6, "end": 16},
{"placeholder": "[EMAIL_ADDRESS_1]", "type": "EMAIL_ADDRESS", "confidence": 0.99, "start": 20, "end": 33}
],
"processing_ms": 12.3
}
```
### Rehydrate
```bash theme={null}
curl -X POST https://api.ambientmeta.com/v1/rehydrate \
-H "X-API-Key: am_live_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "I will contact [PERSON_1] at [EMAIL_ADDRESS_1] tomorrow.", "session_id": "ses_a1b2c3d4e5f6"}'
```
Response:
```json theme={null}
{
"text": "I will contact John Smith at john@acme.com tomorrow.",
"entities_restored": 2,
"processing_ms": 3.1
}
```
## Next Steps
* [Full sanitize API reference](/api-reference/sanitize)
* [Submit corrections](/api-reference/feedback) to improve detection accuracy
* [Create custom patterns](/api-reference/patterns) for org-specific data
* [View insights](/api-reference/insights) and resolve conflicts
* Use with [LangChain](/guides/langchain) or [LlamaIndex](/guides/llamaindex)
* [Python SDK documentation](/sdks/python)
# Python SDK
Source: https://docs.ambientmeta.com/sdks/python
Official Python SDK for AmbientMeta Privacy Gateway
## Installation
```bash theme={null}
pip install ambientmeta
```
## Quick Start
```python theme={null}
from ambientmeta import AmbientMeta
client = AmbientMeta(api_key="am_live_xxx")
# Sanitize
result = client.sanitize("Email john@acme.com")
print(result.sanitized) # "Email [EMAIL_ADDRESS_1]"
# Rehydrate
final = client.rehydrate("Reply to [EMAIL_ADDRESS_1]", result.session_id)
print(final.text) # "Reply to john@acme.com"
```
## Async Support
```python theme={null}
from ambientmeta import AsyncAmbientMeta
client = AsyncAmbientMeta(api_key="am_live_xxx")
result = await client.sanitize("Email john@acme.com")
```
## Configuration
```python theme={null}
client = AmbientMeta(
api_key="am_live_xxx",
timeout=30, # seconds (default: 30)
base_url="https://api.ambientmeta.com", # default API URL
)
```
| Parameter | Type | Default | Description |
| ---------- | ------ | ----------------------------- | -------------------------- |
| `api_key` | string | required | Your AmbientMeta API key |
| `base_url` | string | `https://api.ambientmeta.com` | API base URL |
| `timeout` | float | `30.0` | Request timeout in seconds |
## Methods
### client.sanitize(text, entities=None, mode="sanitize")
Sanitize text by replacing PII with placeholders. Returns `SanitizeResponse` with `.sanitized`, `.session_id`, `.entities_found`, `.entities`, `.processing_ms`, `.redacted`.
Set `mode="redact"` to permanently remove PII. Redacted responses have `session_id=None` and cannot be rehydrated.
### client.rehydrate(text, session\_id)
Restore original PII to sanitized text. Returns `RehydrateResponse` with `.text`, `.entities_restored`, `.processing_ms`.
### client.create\_pattern(name, pattern, description="", examples=None)
Create a custom entity pattern. Returns `PatternResponse` with `.pattern_id`, `.name`, `.status`.
### client.send\_feedback(session\_id, feedback\_type, text\_snippet, expected\_type="")
Submit a single correction. Returns `FeedbackResponse` with `.status`.
### client.get\_insights()
Get pending insights and knowledge depth metrics. Returns `InsightsResponse` with `.pending_conflicts`, `.proposed_improvements`, `.discovered_patterns`, `.knowledge_depth`.
### client.resolve\_insight(insight\_id, resolution, modifications=None, reason=None)
Resolve an insight. `resolution` is `"approve"`, `"refine"`, or `"reject"`.
## Batch Corrections
Chain corrections on a `SanitizeResponse` and submit them all at once:
```python theme={null}
from ambientmeta import AmbientMeta
client = AmbientMeta(api_key="am_live_xxx")
result = client.sanitize("Contact NPI 1234567890 at 555-867-5309")
# Chain corrections
result.correct("[PHONE_NUMBER_1]", "NPI") # Misclassified
result.report_missed("Dr. Smith", "PERSON") # Missed entity
result.report_false_positive("[LOCATION_1]") # Not PII
# Submit all at once
feedback = result.submit_corrections()
print(f"Accepted: {feedback.accepted}")
print(f"Contradictions: {feedback.contradictions_detected}")
```
### Correction Methods on SanitizeResponse
| Method | Description |
| ----------------------------------------------- | ------------------------------------------ |
| `result.correct(placeholder, correct_type)` | Mark a detected entity as misclassified |
| `result.report_missed(span_text, correct_type)` | Report an entity the system missed |
| `result.report_false_positive(placeholder)` | Report a false positive detection |
| `result.submit_corrections()` | Submit all accumulated corrections (sync) |
| `await result.asubmit_corrections()` | Submit all accumulated corrections (async) |
## Error Handling
```python theme={null}
from ambientmeta import AmbientMeta
from ambientmeta.exceptions import RateLimitError, NotFoundError, AuthenticationError
client = AmbientMeta(api_key="am_live_xxx")
try:
result = client.sanitize(user_input)
final = client.rehydrate(response, result.session_id)
except RateLimitError as e:
print(f"Rate limited: {e.message}")
except NotFoundError:
result = client.sanitize(user_input) # Re-sanitize
except AuthenticationError:
print("Check your API key")
```
## Error Classes
| Exception | Description |
| --------------------- | ------------------------------------------------------- |
| `AmbientMetaError` | Base exception for all SDK errors |
| `AuthenticationError` | API key is invalid or missing (401) |
| `RateLimitError` | Rate limit exceeded (429) |
| `NotFoundError` | Resource not found — session, pattern, or insight (404) |
| `ValidationError` | Request failed validation (422) |