Developer Quick Start
Last Updated: 2026-02-16
Get started with the cloak.business API in 5 minutes. This guide covers authentication, basic operations, and code examples in multiple languages.
Recommended: Use our official SDKs for JavaScript/TypeScript or Python instead of raw API calls. The SDKs provide type safety, automatic retry logic, error handling, and client-side encryption.
# JavaScript/TypeScript npm install @cloak-business/sdk # Python pip install cloak-businessSee the SDK Reference for complete documentation.
Table of Contents#
Get Your API Key#
- Sign in at cloak.business
- Go to Dashboard > API Keys
- Click Create API Key
- Copy the key (starts with
cb_)
Important: Store your API key securely. Never commit it to version control or expose it in client-side code.
First API Call#
Test your API key with a simple analysis request:
curl -X POST https://cloak.business/api/presidio/analyze \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Contact John Doe at john@example.com"}'
Expected Response:
{
"results": [
{"entity_type": "PERSON", "start": 8, "end": 16, "score": 0.85, "text": "John Doe"},
{"entity_type": "EMAIL_ADDRESS", "start": 20, "end": 36, "score": 1.0, "text": "john@example.com"}
],
"tokens_charged": 2
}
If you see this response, your API key is working.
Complete Workflow#
A typical PII anonymization workflow has two steps:
Step 1: Analyze#
Detect PII entities in your text:
curl -X POST https://cloak.business/api/presidio/analyze \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Call John at 555-123-4567 or email john@test.com",
"language": "en"
}'
Step 2: Anonymize#
Apply anonymization using the analysis results:
curl -X POST https://cloak.business/api/presidio/anonymize \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Call John at 555-123-4567 or email john@test.com",
"analyzer_results": [
{"entity_type": "PERSON", "start": 5, "end": 9, "score": 0.85},
{"entity_type": "PHONE_NUMBER", "start": 13, "end": 25, "score": 0.95},
{"entity_type": "EMAIL_ADDRESS", "start": 35, "end": 48, "score": 1.0}
],
"operators": {
"PERSON": {"type": "replace"},
"PHONE_NUMBER": {"type": "mask", "masking_char": "*", "chars_to_mask": 7},
"EMAIL_ADDRESS": {"type": "hash"}
}
}'
Result:
{
"text": "Call <PERSON> at ***-***-4567 or email 5d41402abc4b2a76b9719d911017c592",
"items": [...]
}
Code Examples#
Python#
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://cloak.business/api"
def analyze(text: str, language: str = "en") -> dict:
"""Detect PII entities in text."""
response = requests.post(
f"{BASE_URL}/presidio/analyze",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"text": text, "language": language}
)
response.raise_for_status()
return response.json()
def anonymize(text: str, analyzer_results: list, operators: dict = None) -> dict:
"""Anonymize text based on analysis results."""
payload = {
"text": text,
"analyzer_results": analyzer_results
}
if operators:
payload["operators"] = operators
response = requests.post(
f"{BASE_URL}/presidio/anonymize",
headers={"Authorization": f"Bearer {API_KEY}"},
json=payload
)
response.raise_for_status()
return response.json()
def protect_text(text: str, method: str = "replace") -> str:
"""Full workflow: analyze and anonymize text."""
# Step 1: Analyze
analysis = analyze(text)
if not analysis.get("results"):
return text # No PII found
# Step 2: Anonymize
operators = {
result["entity_type"]: {"type": method}
for result in analysis["results"]
}
result = anonymize(text, analysis["results"], operators)
return result["text"]
# Usage
if __name__ == "__main__":
original = "Contact John Doe at john@example.com"
protected = protect_text(original)
print(f"Original: {original}")
print(f"Protected: {protected}")
JavaScript/TypeScript#
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://cloak.business/api";
interface AnalyzerResult {
entity_type: string;
start: number;
end: number;
score: number;
text?: string;
}
interface AnalyzeResponse {
results: AnalyzerResult[];
tokens_charged: number;
}
interface AnonymizeResponse {
text: string;
items: any[];
tokens_charged: number;
}
async function analyze(text: string, language = "en"): Promise<AnalyzeResponse> {
const response = await fetch(`${BASE_URL}/presidio/analyze`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ text, language })
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
async function anonymize(
text: string,
analyzerResults: AnalyzerResult[],
operators?: Record<string, { type: string; [key: string]: any }>
): Promise<AnonymizeResponse> {
const response = await fetch(`${BASE_URL}/presidio/anonymize`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
text,
analyzer_results: analyzerResults,
operators
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
async function protectText(text: string, method = "replace"): Promise<string> {
// Step 1: Analyze
const analysis = await analyze(text);
if (!analysis.results.length) {
return text; // No PII found
}
// Step 2: Build operators
const operators: Record<string, { type: string }> = {};
for (const result of analysis.results) {
operators[result.entity_type] = { type: method };
}
// Step 3: Anonymize
const result = await anonymize(text, analysis.results, operators);
return result.text;
}
// Usage
(async () => {
const original = "Contact John Doe at john@example.com";
const protected_text = await protectText(original);
console.log(`Original: ${original}`);
console.log(`Protected: ${protected_text}`);
})();
Go#
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
apiKey = "YOUR_API_KEY"
baseURL = "https://cloak.business/api"
)
type AnalyzerResult struct {
EntityType string `json:"entity_type"`
Start int `json:"start"`
End int `json:"end"`
Score float64 `json:"score"`
Text string `json:"text,omitempty"`
}
type AnalyzeResponse struct {
Results []AnalyzerResult `json:"results"`
TokensCharged int `json:"tokens_charged"`
}
type AnonymizeResponse struct {
Text string `json:"text"`
TokensCharged int `json:"tokens_charged"`
}
func analyze(text, language string) (*AnalyzeResponse, error) {
payload := map[string]string{"text": text, "language": language}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", baseURL+"/presidio/analyze", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result AnalyzeResponse
json.NewDecoder(resp.Body).Decode(&result)
return &result, nil
}
func anonymize(text string, results []AnalyzerResult) (*AnonymizeResponse, error) {
operators := make(map[string]map[string]string)
for _, r := range results {
operators[r.EntityType] = map[string]string{"type": "replace"}
}
payload := map[string]interface{}{
"text": text,
"analyzer_results": results,
"operators": operators,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", baseURL+"/presidio/anonymize", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result AnonymizeResponse
json.NewDecoder(resp.Body).Decode(&result)
return &result, nil
}
func main() {
text := "Contact John Doe at john@example.com"
analysis, _ := analyze(text, "en")
fmt.Printf("Found %d entities\n", len(analysis.Results))
result, _ := anonymize(text, analysis.Results)
fmt.Printf("Protected: %s\n", result.Text)
}
PHP#
<?php
$apiKey = "YOUR_API_KEY";
$baseUrl = "https://cloak.business/api";
function analyze(string $text, string $language = "en"): array {
global $apiKey, $baseUrl;
$ch = curl_init("$baseUrl/presidio/analyze");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"text" => $text,
"language" => $language
])
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
function anonymize(string $text, array $analyzerResults, array $operators = []): array {
global $apiKey, $baseUrl;
$payload = [
"text" => $text,
"analyzer_results" => $analyzerResults
];
if (!empty($operators)) {
$payload["operators"] = $operators;
}
$ch = curl_init("$baseUrl/presidio/anonymize");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $apiKey",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode($payload)
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
function protectText(string $text, string $method = "replace"): string {
$analysis = analyze($text);
if (empty($analysis["results"])) {
return $text;
}
$operators = [];
foreach ($analysis["results"] as $result) {
$operators[$result["entity_type"]] = ["type" => $method];
}
$result = anonymize($text, $analysis["results"], $operators);
return $result["text"];
}
// Usage
$original = "Contact John Doe at john@example.com";
$protected = protectText($original);
echo "Original: $original\n";
echo "Protected: $protected\n";
Common Patterns#
Pattern 1: Batch Processing#
Process multiple texts efficiently:
def batch_analyze(texts: list[str]) -> dict:
response = requests.post(
f"{BASE_URL}/presidio/batch",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"texts": texts, "language": "en"}
)
return response.json()
# Process 100 texts in one request
texts = ["Text 1...", "Text 2...", ...]
results = batch_analyze(texts)
Pattern 2: Encryption with Deanonymization#
Use encryption for reversible anonymization:
# Anonymize with encryption
result = anonymize(
text="Contact John at john@test.com",
analyzer_results=[...],
operators={
"PERSON": {"type": "encrypt", "key": "your-encryption-key"},
"EMAIL_ADDRESS": {"type": "encrypt", "key": "your-encryption-key"}
}
)
# Later: Deanonymize
original = requests.post(
f"{BASE_URL}/presidio/deanonymize",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"text": result["text"],
"anonymizer_results": result["items"],
"deanonymizers": {
"PERSON": {"type": "decrypt", "key": "your-encryption-key"},
"EMAIL_ADDRESS": {"type": "decrypt", "key": "your-encryption-key"}
}
}
).json()
Pattern 3: Custom Entity Detection#
Add domain-specific patterns:
result = requests.post(
f"{BASE_URL}/presidio/analyze",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"text": "Order #ORD-123456 for customer CUS-789012",
"ad_hoc_recognizers": [
{
"entity_type": "ORDER_ID",
"patterns": [{"name": "order", "regex": "ORD-\\d{6}", "score": 0.9}]
},
{
"entity_type": "CUSTOMER_ID",
"patterns": [{"name": "customer", "regex": "CUS-\\d{6}", "score": 0.9}]
}
]
}
).json()
Pattern 4: Error Handling#
Robust error handling:
def safe_analyze(text: str) -> dict | None:
try:
response = requests.post(
f"{BASE_URL}/presidio/analyze",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"text": text},
timeout=30
)
if response.status_code == 401:
raise ValueError("Invalid API key")
elif response.status_code == 402:
raise ValueError("Insufficient tokens")
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", 60)
raise ValueError(f"Rate limited. Retry after {retry_after}s")
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print("Request timed out")
return None
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
Next Steps#
Now that you have the basics working:
- SDK Reference - Official JavaScript & Python SDKs with client-side encryption
- API Reference - Complete endpoint documentation
- Entity Inventory - Browse 390+ entity types
- Presets - Use regional presets
- MCP Integration - Integrate with AI tools
- Structured Data - Process CSV/JSON files
Environment Variables#
For production, use environment variables:
# .env
CLOAK_API_KEY=cb_your_api_key_here
CLOAK_API_URL=https://cloak.business/api
import os
API_KEY = os.environ.get("CLOAK_API_KEY")
BASE_URL = os.environ.get("CLOAK_API_URL", "https://cloak.business/api")
Document maintained by cloak.business