---
name: agiletrust-tokenization
description: AgileTrust Tokenization API integration helper. Use this skill when the user asks to tokenize, detokenize, integrate with AgileTrust, implement format-preserving encryption (FPE), work with FF1 or FF3-1 algorithms, protect PANs, RUTs, DNIs, national IDs, emails, phone numbers, names, or any PII that must keep its original format. Also use when the user asks about X-API-Key auth, encoding modes (utf8/latin1/numeric), tweaks, or how to call /tokenize or /detokenize endpoints.
version: 1.0.0
user-invocable: true
---

# AgileTrust Tokenization API — Integration Guide

**AgileTrust Tokenization** replaces sensitive data (names, IDs, card numbers, emails) with
cryptographically secure, format-identical tokens using Format-Preserving Encryption (FPE)
per NIST SP 800-38G.

Token properties:
- Same length and structure as the original value
- Letters and digits are encrypted; symbols (`-`, ` `, `@`, `.`, etc.) are preserved in place
- Fully reversible with the same key + encoding + tweak
- Deterministic: same input + key + tweak → same token every time

---

## 1. Setup

Set these environment variables before running any of the examples below:

```bash
export AGILETRUST_API_URL="http://localhost:8000"   # or https://api.agiletrust.io/v1
export AGILETRUST_API_KEY="tok_live_..."            # obtain from your tenant owner/admin
```

To get an API key: log into the Admin Console → Applications → select your app → API Keys → Create.
Your tenant owner or admin role is required; viewer role cannot create keys.

---

## 2. Quick Start

**Tokenize:**
```bash
curl -sX POST "$AGILETRUST_API_URL/tokenize" \
  -H "X-API-Key: $AGILETRUST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plaintext":"Juan Pérez-García","encoding":"utf8"}'
# {"token":"Ñkrp Çmevq-Ĥapcw","algorithm":"FF3-1/AES","encoding":"utf8"}
```

**Detokenize (reverse):**
```bash
curl -sX POST "$AGILETRUST_API_URL/detokenize" \
  -H "X-API-Key: $AGILETRUST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"token":"Ñkrp Çmevq-Ĥapcw","encoding":"utf8"}'
# {"plaintext":"Juan Pérez-García"}
```

Health check (no auth):
```bash
curl -s "$AGILETRUST_API_URL/health"
# {"status":"ok","version":"0.2"}
```

---

## 3. Encoding — Choose the Right One

| Encoding | Alphabet | Max input chars | Token chars | Best for |
|----------|----------|-----------------|-------------|----------|
| `utf8` *(default)* | ~65k Unicode letters + digits (BMP) | 256 | Unicode | Names, free text, emails |
| `latin1` | ~223 Latin-1 letters + digits (≤ U+00FF) | 256 | Latin-1 range | Western European names, byte-range constrained systems |
| `numeric` | `0123456789` | 16 | Digits only | PANs, RUTs, phones, national IDs |

Encoding rule: **non-alphabet characters pass through unchanged.** A space, hyphen, `@`, or `(` in the input appears at the exact same position in the token. Only letters and digits are encrypted.

---

## 4. Algorithm — FF3-1 vs FF1

The algorithm is configured **per application** in the Admin Console when the application is created.
As an integrator you do not choose the algorithm at request time — you need to match the `tweak` format the application was set up with.

| | FF3-1 | FF1 |
|---|---|---|
| Standard | NIST SP 800-38G Rev 1 (2019) | NIST SP 800-38G (2016) |
| Tweak length | **Exactly 7 bytes** (14 hex chars) | **0 to 256 bytes** (variable) |
| Min input length (numeric) | 6 digits | 2 digits |
| Max segment length (numeric) | 56 digits | Unlimited |
| Relative speed | Baseline | ~1.6× slower (short) / ~1.0× (long) |
| Best for | General PII — PAN, RUT, phone, names | Short inputs, long context tweaks, HSM compat |

**Default is FF3-1.** Choose FF1 only if your application was created with that setting.

> Full pros/cons matrix: see `references/algorithms.md`

---

## 5. Tweak — Context Binding

A **tweak** is a field-level context that prevents cross-field token correlation.
Same plaintext tokenized with different tweaks produces different tokens.

```bash
# "name" tweak: ASCII of "name" zero-padded to 7 bytes → 6e616d65000000
curl -sX POST "$AGILETRUST_API_URL/tokenize" \
  -H "X-API-Key: $AGILETRUST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plaintext":"María López","encoding":"utf8","tweak":"6e616d65000000"}'
```

Rules:
- **FF3-1**: tweak must be exactly **14 hex characters** (7 bytes). If omitted → `"00000000000000"`.
- **FF1**: tweak can be any even-length hex string up to 512 hex chars (256 bytes). If omitted → empty/zero.
- **Critical**: the exact same tweak must be used when detokenizing. A different tweak returns HTTP 200 with wrong plaintext — there is no error indicator (FPE security property).

Common tweak values:
| Field | Tweak hex |
|---|---|
| Generic / none | `00000000000000` |
| name | `6e616d65000000` |
| email | `656d61696c0000` |
| rut / dni | `7275740000000000` (truncate to 7 bytes → `72757400000000`) |
| phone | `70686f6e650000` |
| card / pan | `63617264000000` |

---

## 6. Error Handling

| HTTP status | Body | Cause |
|---|---|---|
| `200` | `{"token": ...}` | Success |
| `400` | `{"detail": "..."}` | Engine error (bad chars, wrong tweak length for algorithm) |
| `401` | `{"error": "Missing API key."}` | No `X-API-Key` header |
| `402` | `{"detail": "Trial period..."}` | Subscription expired |
| `403` | `{"error": "Invalid API key."}` or `"Application is disabled."` | Bad or revoked key |
| `422` | `{"error": "field: msg"}` | Pydantic validation: missing `plaintext`/`token`, bad encoding, bad tweak hex, input too long |
| `429` | `{"detail": "Monthly operation quota exceeded..."}` | Plan quota hit |
| `500` | `{"error": "Internal server error."}` | Unhandled exception |
| `503` | `{"detail": "Service temporarily unavailable."}` | DB or KMS unreachable |

**Critical caveat — wrong tweak/key on detokenize:**
The API returns `HTTP 200` with garbage plaintext when the wrong tweak, key, or encoding is used.
This is a property of FPE — the algorithm always produces a valid-looking token/plaintext.
Always validate the recovered plaintext in your application (e.g., check RUT verifier digit, Luhn for PAN).

> Full error catalogue with reproduction examples: see `references/error-codes.md`

---

## 7. Code Samples

### Python
```python
import os, requests

BASE = os.getenv("AGILETRUST_API_URL", "http://localhost:8000")
KEY  = os.environ["AGILETRUST_API_KEY"]
HDR  = {"X-API-Key": KEY}

def tokenize(plaintext: str, encoding: str = "utf8", tweak: str | None = None) -> str:
    body = {"plaintext": plaintext, "encoding": encoding}
    if tweak:
        body["tweak"] = tweak
    r = requests.post(f"{BASE}/tokenize", headers=HDR, json=body, timeout=5)
    r.raise_for_status()
    return r.json()["token"]

def detokenize(token: str, encoding: str = "utf8", tweak: str | None = None) -> str:
    body = {"token": token, "encoding": encoding}
    if tweak:
        body["tweak"] = tweak
    r = requests.post(f"{BASE}/detokenize", headers=HDR, json=body, timeout=5)
    r.raise_for_status()
    return r.json()["plaintext"]

# Usage
token = tokenize("13301430-6", encoding="numeric")
plain = detokenize(token, encoding="numeric")
assert plain == "13301430-6"
```

### Node.js
```js
const BASE = process.env.AGILETRUST_API_URL ?? "http://localhost:8000";
const KEY  = process.env.AGILETRUST_API_KEY;

async function tokenize(plaintext, { encoding = "utf8", tweak } = {}) {
  const body = { plaintext, encoding, ...(tweak && { tweak }) };
  const res = await fetch(`${BASE}/tokenize`, {
    method: "POST",
    headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return (await res.json()).token;
}

async function detokenize(token, { encoding = "utf8", tweak } = {}) {
  const body = { token, encoding, ...(tweak && { tweak }) };
  const res = await fetch(`${BASE}/detokenize`, {
    method: "POST",
    headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return (await res.json()).plaintext;
}
```

> Full runnable scripts (curl/Python/JS): see `assets/examples/`

---

## 8. Common Integration Patterns

| PII Type | Encoding | Algorithm | Tweak strategy | Key notes |
|----------|----------|-----------|----------------|-----------|
| **Email** | `utf8` | FF3-1 | `656d61696c0000` (field "email") | Tokenize **local-part only** (`user` in `user@domain.com`). Preserve `@` and domain for routing. If domain is also PII, tokenize with a different tweak. |
| **RUT / DNI** (CL, AR) | `numeric` | FF3-1 (≥6 digits) or FF1 (<6) | `72757400000000` ("rut") | Pass full field (`12345678-9`); hyphen and check digit pass through unchanged. **Recalculate the check digit** after tokenizing — the FPE result may not satisfy the Modulo-11 constraint. |
| **Credit card (PAN)** | `numeric` | FF3-1 | `63617264000000` or BIN (first 6) | Tokenize **only digits 7–15** (middle block). Preserve BIN (first 6) + last digit for Luhn routing. Resulting token is PCI-DSS compliant. |
| **Phone number** | `numeric` | FF3-1 | `70686f6e650000` ("phone") | Preserve country prefix (`+56 9`); tokenize subscriber digits. Spaces and `+` pass literally. |
| **Full name** | `utf8` | FF3-1 | `6e616d65000000` ("name") | Tokenize **first and last names separately** to preserve partial-match queries (`WHERE apellido = token`). Spaces, accents, and special chars are preserved. |
| **Date** (`YYYY-MM-DD`) | `numeric` | FF1 (8 digits < FF3-1 min) | field name as tweak | The 8 digits are tokenized; `-` separators pass through. **The result is not a valid date** (e.g. `2847-99-13`). If you need valid dates, tokenize year/month/day separately. |
| **IBAN / bank account** | `numeric` (BBAN digits) or `latin1` (full) | FF3-1 | `62616e6b000000` ("bank") | Preserve the 2-char country code and 2 check digits; tokenize the BBAN. |
| **IP address** | `numeric` | FF1 (octets = 1–3 digits, below FF3-1 minimum) | `69700000000000` ("ip") | Tokenize each octet independently, preserving `.` separators. |

> Full use-case recipes with curl + Python examples: see `references/use-cases.md`

---

## 9. Reference Files

This skill includes the following reference documents (loaded by Claude on demand):

| File | Contents |
|------|---------|
| `references/api-reference.md` | Full endpoint specs, request/response schemas, all fields and types |
| `references/algorithms.md` | FF3-1 vs FF1 detailed comparison, capability/limit matrices, decision guide |
| `references/error-codes.md` | Every HTTP status code with reproduction curl examples |
| `references/use-cases.md` | Per-PII-type recipes: email, RUT/DNI, PAN, phone, name, date, IBAN, IP |
| `assets/openapi.yaml` | Full OpenAPI 3.0.3 spec (offline copy) |
| `assets/examples/tokenize.sh` | Runnable bash/curl script covering all encodings |
| `assets/examples/tokenize.py` | Python `requests` script with tokenize/detokenize helpers |
| `assets/examples/tokenize.js` | Node.js `fetch` script, ESM module style |
