REST Channel
Standard HTTP/JSON. If your app can make a GET request, it can query the directory. The REST channel comes with an OpenAPI spec, interactive docs, conditional requests, and streaming exports.
Quick start
Section titled “Quick start”Scribe enables the REST channel by default. Key endpoints:
| URL | What you get |
|---|---|
/api | OpenAPI UI (browser) or spec (JSON/YAML via content negotiation) |
/api/entries/{type} | Collection search |
/api/entries/{type}/{id} | Single entry lookup |
/api/entries/{type}/{id}/changes | Per-entry change history |
/api/changes | Global change feed |
Browse /api in a browser for interactive API docs with code samples, live testing, and schema details. Download the spec at /api.json or /api.yaml.
Entry type naming guardrails
Section titled “Entry type naming guardrails”REST paths and OpenAPI identifiers are derived from entry type names using singular/plural normalization. Startup enforces strict uniqueness across:
- REST paths (
/api/entries/{type},/api/entries/{type}/{id},/api/entries/{type}/{id}/changes) - OpenAPI
operationIdvalues (search*,lookup*,get*Changes) - OpenAPI schema component names (
Entry_*,SearchResponse_*)
If two configured types collide after normalization (for example user and users), startup fails before serving traffic.
OpenAPI component names are sanitized to a conservative format for code generators: ^[A-Za-z][A-Za-z0-9_]*$. Reserved tokens (for example class) are suffixed (for example class_type). If sanitization causes a component-name collision, startup fails.
Querying
Section titled “Querying”Filter
Section titled “Filter”Filters auto-detect format. Use whichever fits your context:
| Format | Detection | Example |
|---|---|---|
| JSON | Starts with { or [ | {"cn":"john"} |
| LDAP | Starts with ( | (cn=john*) |
| FleX | Default | cn = john |
| SCIM | Fallback | cn eq "john" |
# Spaces in filter values are URL-encoded by curl when using --data-urlencode,# or quote the entire URL as shown herecurl "/api/entries/users?filter=department%3DEngineering%20AND%20active%3Dtrue"Fields
Section titled “Fields”| Value | Returns |
|---|---|
cn,mail,sn | Specific attributes |
* | All user attributes |
+ | All operational attributes |
1.1 | No attributes (IDs only) |
?sort=cn # Ascending?sort=-cn # Descending?sort=sn,-cn # Multiple keysPagination
Section titled “Pagination”Relay-style cursor pagination:
| Parameter | Direction |
|---|---|
first / after | Forward |
last / before | Backward |
limit | Alias — resolves to first or last based on cursor |
# First pagecurl "/api/entries/users?first=25"
# Next page (use endCursor from previous response)curl "/api/entries/users?first=25&after=eyJjbiI6Impv..."ID formats
Section titled “ID formats”Lookup endpoints accept any identifier format. DN values must be URL-encoded in paths.
Conditional requests
Section titled “Conditional requests”Poll efficiently with ETags:
# First request returns ETagcurl -I "/api/entries/users/$UUID"# ETag: W/"abc123..."
# Subsequent requests — 304 when unchangedcurl -H 'If-None-Match: W/"abc123..."' "/api/entries/users/$UUID"If-Modified-Since is also supported. Search responses include Cache-Control: no-store; lookup responses include Cache-Control: private, max-age=0, must-revalidate.
Change history
Section titled “Change history”Query changes via the global feed (/api/changes) or per-entry (/api/entries/{type}/{id}/changes).
# Changes in the last 24 hourscurl "/api/changes?range=24h&first=50"
# MODIFY events that changed the mail attributecurl "/api/changes?type=modify&affects=mail&since=this week"Time ranges accept temporal references: ISO timestamps, durations, yesterday, last 3 days, etc.
Point-in-time lookup
Section titled “Point-in-time lookup”View an entry at any moment:
curl "/api/entries/users/$ID?at=2024-01-15T10:00:00Z"curl "/api/entries/users/$ID?at=now-1h"Change events support three data formats. Use ?include=nodes,patch,pageInfo to select which parts to return. For count-only queries (no data fetch), use ?include=count.
Export
Section titled “Export”Stream search results to a file by adding filename to any search request:
curl -OJ "/api/entries/users?filename=users.csv&fields=cn,mail,department"In export mode, Scribe bypasses pagination and streams the full result set.
| Format | Extension | Notes |
|---|---|---|
| CSV | .csv | Excel-compatible, RFC 4180 |
| TSV | .tsv | Tab-delimited |
| JSON | .json | Streaming JSON array |
| NDJSON | .ndjson | Newline-delimited JSON |
| JSON-seq | .json-seq | RFC 7464 |
| JSON-LD | .jsonld | With @id/@type |
| LDIF | .ldif | LDAP interchange format |
Exports require an explicit field list — wildcards (*, +) are not allowed. Pass format options as JSON via the export parameter:
# Custom delimiter and null handlingcurl -OJ '/api/entries/users?filename=data.csv&fields=cn,mail&export={"delimiter":"|","nulls":"N/A"}'See Configuration Reference for export limits, delimited format options, and all other REST settings.
Request preferences
Section titled “Request preferences”Control how the API handles unknown attributes with Prefer: handling=strict (returns 400 with the unknown names) or Prefer: handling=lenient (default — ignores unknowns silently).
See Signals for metrics, Error Handling for error codes.