ОUЯUKΛ / Documentation
DE EN

Ouruka ID Service

Ouruka is a REST API service for generating unique, structured IDs. Instead of building your own counters, date logic, and uniqueness guarantees, you define a schema (e.g. ORD-{DATE:YYYY}-{COUNTER:6}) and fetch ready-made IDs via API — sequential, collision-free, even under heavy load.

  • Base URL: https://ouruka.com
  • Authentication: API key in the header X-API-KEY
  • API reference: /swagger/ (machine-readable, interactive)

Typical Use Cases

  • PIM: Consistent article and product numbers across multiple channels.
  • ERP: Document, order, and delivery note numbers in a uniform format.
  • E-Commerce: Order, customer, and transaction numbers.
  • Logistics: Shipment numbers, pallet IDs, shipping labels.
  • Healthcare: Patient record, sample, and process numbers.
  • Manufacturing: Serial, batch, and quality IDs.

The rest of this documentation walks you through authentication, schemas, all placeholders, the add-on services (UID, Keyify, Mappings), and a hands-on example library.

Quickstart

A few steps to your first generated ID.

Step 1 — Get an API Key

Your account (tenant) is provisioned by Ouruka. When you sign up, you receive an api_key once. This key identifies your tenant and is required for all requests — keep it safe (see Authentication).

Step 2 — Create a Schema

A schema defines the format of your IDs. You specify which building blocks (date, counter, fixed text, variables) make up an ID.

curl -X POST https://ouruka.com/v2/schemas \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"name":"order","pattern":"ORD-{DATE:YYYY}{DATE:MM}-{COUNTER:5}","start_value":1}'

This produces IDs in the format ORD-202601-00001.

Step 3 — Generate an ID

Use the schema name to fetch the next ID. Ouruka ensures every ID is unique.

curl -X POST https://ouruka.com/v2/generate/order -H "X-API-KEY: $KEY"
{ "id": "ORD-202601-00001" }

Each subsequent call returns the next number (…-00002, …-00003, …).

🔧 Under the hood: Uniqueness is guaranteed by the server, not your code. The counter is incremented atomically, so even thousands of concurrent requests from multiple systems will never receive the same number twice. You don't need to worry about locks or race conditions — a single POST per ID is all it takes.

Try it interactively: https://ouruka.com/swagger/ — click "Authorize", enter your API key, and test all endpoints directly in your browser.

Authentication

All endpoints that read or write data require a valid API key in the HTTP header:

X-API-KEY: <your API key>

Exceptions (accessible without a key): GET /health and GET /v2/changelog.

The API key uniquely identifies your tenant and controls access to all your schemas, counters, mappings, and history data. If the key is missing or invalid, the service responds with 401 Unauthorized.

🔧 Under the hood: There is no "forgot your key" flow — the key is both identity and permission, and Ouruka stores it only as a SHA-256 hash (the plain text exists only on your side). This means the key can never be "read back"; if it is lost, simply generate a new one via rotation. Internally there are two levels: the tenant key (your data) and a separate master key (Ouruka backend only, for administration) — the two are strictly separated.

Safe Handling

  • Never store the API key in source code or versioned files.
  • Use environment variables or a secret manager (e.g. AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets).
  • Do not share it with third parties — anyone who knows the key can generate IDs in your name.
  • Transmission is exclusively over HTTPS/TLS.
  • Ouruka never stores keys in plain text, only as a cryptographic hash (SHA-256).

Key Rotation

If a key has been compromised — or just as a precaution — you can rotate it at any time. The new key is returned once, and the old one becomes immediately invalid:

curl -X POST https://ouruka.com/v2/me/rotate-key -H "X-API-KEY: $KEY"
{ "status": "rotated", "api_key": "…new key…",
  "message": "The previous key is now invalid." }

Store the new key securely right away — it only appears in this one response.

Schemas & Placeholders

A schema is the template for an ID series — you define the format once and Ouruka delivers sequential IDs from that point on. A pattern consists of fixed text plus placeholders, e.g. ART-{DATE:YY}{DATE:MM}-{COUNTER:4}ART-2603-0017.

🔧 Under the hood: A schema is bound to a tenant and has its own independent counter, its own history, and its own pattern. Schemas are immutable: once created, the pattern cannot be changed (409 Conflict on a repeated POST). This is intentional — a running number series should not change its format mid-stream. To "change" a schema, delete it and create a new one (the counter then resets to start_value).


Date Placeholders

Insert the current date automatically — useful for seeing at a glance when something was created.

Placeholder Result Meaning
{DATE:YYYY} 2026 Four-digit year
{DATE:YY} 26 Two-digit year (saves characters)
{DATE:MM} 03 Month (01–12)
{DATE:DD} 18 Day (01–31)

Mixed real-world examples:

Industry Pattern Example ID
Logistics – Bills of lading FB-{DATE:YYYY}-{COUNTER:6} FB-2026-000134
Healthcare – Lab samples LAB-{DATE:YYYY}-{COUNTER:5} LAB-2026-00891
Fashion – Seasonal items JKT-{DATE:YY}{DATE:MM}-{COUNTER:4} JKT-2603-0056
Food service – Daily receipts BON-{DATE:YYYY}{DATE:MM}{DATE:DD}-{COUNTER:4} BON-20260318-0047
Events – Ticket codes TICKET-{DATE:YY}-{COUNTER:5} TICKET-26-00042

🔧 Under the hood: The date is resolved at generation time in UTC (not when the schema is created). A daily prefix like {DATE:YYYY}{DATE:MM}{DATE:DD} works great as a "bucket" for statistics and archiving. Order and separators are freely combinable.


Counters — {COUNTER:N}

The heart of every sequential number: an incrementing counter, left-padded with zeros. N is the number of digits ({COUNTER:4}0001, 0002, …).

🔧 Under the hood: The counter is incremented via an atomic Redis operation — even with many concurrent requests from different systems, each value is assigned exactly once (load test: 500,000 IDs, 0 duplicates). N is only the minimum width: if more numbers are generated than digits allow, the number grows ({COUNTER:4} simply outputs 10000 after 9999). The optional start_value (default 1) is set when creating the schema — ideal for continuing a number series from a legacy system seamlessly.

Industry Pattern start_value Example ID
E-Commerce – Orders ORD-{DATE:YYYY}-{COUNTER:6} 1 ORD-2026-000342
Pharma – Batches CH-{DATE:YYYY}-{COUNTER:5} 10000 CH-2026-10047
Accounting – Invoices (migration) INV-{COUNTER:6} 4711 INV-004711

Auto-Reset (Interval Counters)

Many number sequences need to restart periodically from 1 (annual invoice numbers, daily tickets …). An appended interval does exactly that — automatically, without a cron job: {COUNTER:N:daily|monthly|quarterly|yearly}.

Pattern Behaviour
INV-{DATE:YYYY}-{COUNTER:5:yearly} 2026: INV-2026-00001 … · 2027: INV-2027-00001 (reset)
T-{DATE:YYYY}/{DATE:MM}-{COUNTER:4:monthly} July: T-2026/07-0001 · August: T-2026/08-0001
Q-{DATE:YYYY}-{COUNTER:3:quarterly} Q1: Q-2026-001 · Q2: restarts at 001

🔧 Under the hood: Technically the current period becomes part of the Redis counter key (…:2026-03 for monthly). When the period changes, a new key is created → the counter automatically resets to start_value. Old period keys expire via TTL. Any start_value applies at every reset (e.g. reset to 1000 instead of 1).


Variables — {VAR1} to {VAR4}

Values you supply per request (e.g. country code, product group) — so your IDs carry context-specific information.

  • {VAR1} — insert value directly.
  • {VAR1:-} / {VAR2:/} — value with a leading separator (-, /, …).
Industry Pattern Input Example ID
Logistics – Destination SEND-{DATE:YYYY}-{VAR1:-}{COUNTER:6} var1=DE SEND-2026-DE-000071
PIM – Category ART-{VAR1:-}{DATE:YY}-{COUNTER:5} var1=ELEK ART-ELEK-26-00019
Multiple variables {VAR1}-{VAR2:-}{COUNTER:4} var1=DE, var2=B2B DE-B2B-0001

🔧 Under the hood: The separator only appears if the value is not empty. If a variable is empty, both the placeholder and the separator are cleanly omitted — no double separators (ORD--0001). This lets a single pattern handle "with and without a suffix" simultaneously. Pass variables in the request body: {"var1":"DE","var2":"B2B"}.


Mappings — {MAP:table}

Translates a plain-text value into a code: you say "white", Ouruka inserts the stored code "101". This keeps code mappings in one place instead of scattered across every system.

Pattern Table color Request Example ID
{COUNTER:4}{MAP:color} white→101 {"mappings":{"color":"white"}} 0001101
ART-{MAP:color}-{MAP:size} white→101, S→202 {"mappings":{"color":"white","size":"S"}} ART-101-202

🔧 Under the hood: Mapping tables are maintained per tenant (POST /v2/mappings/{table}). When generating, you pass the plain-text key; Ouruka resolves it at ID-generation time. If the value is not in the table, you get 400 Bad Request with a clear message — so a typo is caught immediately rather than producing a wrong ID. Ideal when PIM, ERP, and shop all need the same color or size code.


Schema type hash (Fingerprint)

Instead of a counter, a hash schema generates a deterministic code from defined fields — always the same output for the same input. Perfect for duplicate detection.

{ "name": "fingerprint", "type": "hash", "fields": ["iban","name"], "hash_length": 64 }

🔧 Under the hood: Ouruka sorts the fields alphabetically, concatenates them as key=value|…, and computes a SHA-256 hash, truncated to hash_length (allowed: 16, 32, 40, 64). The result is deterministic and order-independent: {"iban":"DE..","name":"ACME"} produces the same hash as {"name":"ACME","iban":"DE.."}. When generating, all configured fields are required (otherwise 400). Use case: same supplier/record → same fingerprint → recognisable as already known, without storing raw data in plain text.

Example Library

Concrete requests for every feature. All examples use https://ouruka.com as the base and $KEY as a placeholder for your tenant API key.

All protected endpoints require the header X-API-KEY: $KEY.


1. Authentication

Every request (except /health and /v2/changelog) requires your API key in the header:

curl https://ouruka.com/v2/me -H "X-API-KEY: $KEY"

Missing or incorrect key → 401 Unauthorized.


2. Create a Schema

A schema is the template for an entire ID series: you define the pattern once, and every subsequent generation call returns the next ID from it. A schema is immutable after creation (to change it: delete and recreate) — this keeps a number series internally consistent.

curl -X POST https://ouruka.com/v2/schemas \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"name":"order","pattern":"ORD-{DATE:YYYY}-{COUNTER:6}","start_value":1}'

Response (with a preview of the first ID, without consuming the counter):

{ "name": "order", "pattern": "ORD-{DATE:YYYY}-{COUNTER:6}", "preview": "ORD-2026-000001" }

List & Delete Schemas

# All your schemas — including preview and last generated ID
curl https://ouruka.com/v2/schemas -H "X-API-KEY: $KEY"

# Delete a schema (also removes its counter and history entries)
curl -X DELETE https://ouruka.com/v2/schemas/order -H "X-API-KEY: $KEY"

🔧 Under the hood: GET /v2/schemas returns preview, last_generated, and last_generated_at per schema — useful for a dashboard. Since schemas are immutable, delete + recreate is the way to change a pattern; the counter then resets to start_value.


3. Generate IDs

Simple Counter with Date

curl -X POST https://ouruka.com/v2/generate/order -H "X-API-KEY: $KEY"
{ "id": "ORD-2026-000001" }

The next call returns ORD-2026-000002, and so on.

Start Value (e.g. Legacy Migration)

Schema KND-{COUNTER:5} with "start_value": 1000 → first ID:

{ "id": "KND-01000" }

Counter with Auto-Reset (Interval)

Many number sequences need to restart periodically from 1 (e.g. annual invoice numbers). An interval counter does exactly that — automatically, no cron job or manual reset needed. Pattern INV-{DATE:YYYY}-{COUNTER:5:yearly} resets the counter each year:

Time Result
2026 INV-2026-00001, INV-2026-00002, …
2027 INV-2027-00001 (reset)

Supported intervals: daily, monthly, quarterly, yearly.

Runtime Variables (with Optional Separator)

Schema ART-{VAR1}-{VAR2}-{COUNTER:4}:

curl -X POST https://ouruka.com/v2/generate/article \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"var1":"RED","var2":"XL"}'
{ "id": "ART-RED-XL-0001" }

Optional separator {VAR1:-}: if the variable is empty, both the placeholder and the separator are omitted. Pattern ORD-{DATE:YYYY}{VAR1:-}{COUNTER:5} → with var1=DE: ORD-2026-DE-00001, without: ORD-2026-00001.

Attribute Codes from Mapping Table

Pattern {COUNTER:4}{MAP:color} (table color: white→101):

curl -X POST https://ouruka.com/v2/generate/article \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"mappings":{"color":"white"}}'
{ "id": "0001101" }

4. Hash Schema (Deterministic Fingerprint)

Generates the same hash from defined fields every time — ideal for duplicate detection.

curl -X POST https://ouruka.com/v2/schemas \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"name":"fingerprint","type":"hash","fields":["iban","name"],"hash_length":64}'

Generate (all fields required; order does not matter):

curl -X POST https://ouruka.com/v2/generate/fingerprint \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"iban":"DE12...","name":"ACME Ltd"}'
{ "id": "9f2c4a1b…" }

Same input → same 64-character hash every time. Allowed lengths: 16, 32, 40, 64.


5. Random IDs (UID)

No schema needed, purely random. Two variants, lengths 16 / 32 / 64 / 128 / 256 / 512.

curl -X POST https://ouruka.com/v2/uid/numeric \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" -d '{"length":16}'
{ "id": "8391027465018273", "length": 16, "type": "numeric" }

/v2/uid/alpha returns alphanumeric UIDs. UIDs are stored masked in history (only the last 5 characters visible).


6. Text to Key (Keyify)

Converts arbitrary text into a URL- and system-safe key (umlauts transliterated, special characters removed).

curl -X POST https://ouruka.com/v2/keyify \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"input":"Red Size – XL 42!"}'
{ "input": "Red Size – XL 42!", "key": "Red_Size_XL_42" }

7. Mapping Tables

With mapping tables you only need to know the plain-text value in your application (e.g. "white") — Ouruka inserts the stored code (e.g. "101") at generation time. This keeps code mappings centralised in one place instead of maintained separately in every connected system.

# Create/update entry
curl -X POST https://ouruka.com/v2/mappings/color \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"key":"white","value":"101"}'

# List all tables / entries of a table
curl https://ouruka.com/v2/mappings         -H "X-API-KEY: $KEY"
curl https://ouruka.com/v2/mappings/color   -H "X-API-KEY: $KEY"

# Delete a single entry / entire table
curl -X DELETE https://ouruka.com/v2/mappings/color/white -H "X-API-KEY: $KEY"
curl -X DELETE https://ouruka.com/v2/mappings/color       -H "X-API-KEY: $KEY"

8. History (with Pagination)

curl "https://ouruka.com/v2/history?limit=50&offset=0" -H "X-API-KEY: $KEY"
[
  { "id": "ORD-2026-000002", "schema_name": "order",
    "pattern_used": "ORD-{DATE:YYYY}-{COUNTER:6}",
    "requested_at": "2026-07-26T10:15:03.421Z", "source_ip": "203.0.113.7" }
]

Page through: ?limit=50&offset=50 returns the next page.


9. Your Account & Limits

curl https://ouruka.com/v2/me -H "X-API-KEY: $KEY"
{
  "package": "Orbit",
  "limits": { "max_schemas": 5, "monthly_ids": 2000 },
  "usage":  { "schemas": 2, "ids_this_month": 341 }
}

10. Rotate API Key

Generates a new key; the old one becomes immediately invalid. Store the new key securely right away — it only appears once.

curl -X POST https://ouruka.com/v2/me/rotate-key -H "X-API-KEY: $KEY"
{ "status": "rotated", "api_key": "…new key…", "message": "The previous key is now invalid." }

11. Error Overview

Situation Status
Missing / invalid API key 401
Invalid input (e.g. disallowed UID length) 400
Limit reached (schemas or monthly quota) 403
Schema already exists (immutable) 409
Too many requests to public endpoints (60/min) 429

Error responses are JSON, e.g.:

{ "error": "Schema limit reached (5/5). Delete an existing schema or upgrade your package." }

Random IDs (UID)

Sometimes you don't want a structured number — you just need a unique random code, e.g. for tokens, voucher PINs, or anonymous identifiers. That's exactly what UIDs are: purely random, no date or counter.

🔧 Under the hood: UIDs carry no semantics and require no schema. They are generated from cryptographic randomness; the longer, the smaller the (already tiny) collision probability. UIDs count towards the monthly ID quota and are stored masked in history (only the last 5 characters visible) — so a sensitive code never appears in full in the log.

Allowed lengths: 16, 32, 64, 128, 256, 512. (Other values → 400 Bad Request.)

Numeric UIDs — POST /v2/uid/numeric

Digits only (0–9) — great when people need to read the code over the phone or when it must go into a numeric field.

curl -X POST https://ouruka.com/v2/uid/numeric \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" -d '{"length":16}'
{ "id": "8391027465018273", "length": 16, "type": "numeric" }
Use case Length Example
Voucher PIN 16 7392048117263540
Support ticket reference 16 4410298356170022
Barcode base 32 82940173625481930274615098…

Alphanumeric UIDs — POST /v2/uid/alpha

Letters + digits (a–z, A–Z, 0–9) — more compact and harder to guess at the same length.

curl -X POST https://ouruka.com/v2/uid/alpha \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" -d '{"length":32}'
Use case Length Example
Session token 32 aX7kP2mRnQz9wLvBcT4eYdJ0hF6sN1uG
Anonymisation ID (study) 16 Kf3mX9pLrT2nVqBw
API / webhook secret 64 Zp3… (64 characters)

🔧 Under the hood: The alphanumeric character space is significantly larger than the numeric one — a 16-character alpha UID is far "stronger" than a 16-character numeric UID. Rule of thumb: numeric for human-friendly codes, alphanumeric for technical keys/tokens. For security-critical secrets, prefer 64+ characters.

String Conversion (Keyify)

Keyify turns arbitrary text into a clean, system-safe key — for example, a product name into a technical identifier. Small, but handy.

🔧 Under the hood: Keyify is stateless (no schema, no stored ID) and is designed to generate consistent, repeatable keys from editorially maintained plain text — for example as database keys, internal codes, or as a building block before the actual ID generation. Same input always produces the same key.

POST /v2/keyify (authentication required).

What happens

  • Spaces → underscore (_)
  • German umlauts resolved: ä→ae, ö→oe, ü→ue, Ä→Ae, Ö→Oe, Ü→Ue, ß→ss
  • Other special characters are removed
  • Case is preserved
  • Multiple consecutive underscores are merged
curl -X POST https://ouruka.com/v2/keyify \
  -H "X-API-KEY: $KEY" -H "Content-Type: application/json" \
  -d '{"input":"Red Size – XL 42!"}'
{ "input": "Red Size – XL 42!", "key": "Red_Size_XL_42" }

Examples

Input Result
Black Leather Jacket Size XL Black_Leather_Jacket_Size_XL
Kühlschränke & Gefriergeräte Kuehlschraenke_Gefriergeraete
Sports / Outdoor Sports_Outdoor
Müller & Söhne GmbH Mueller_Soehne_GmbH
Red Size – XL 42! Red_Size_XL_42

🔧 Under the hood: German umlauts and ß are deliberately transliterated (ä→ae, ß→ss, …); other special characters are mapped to a base character where possible, otherwise removed. The key consists exclusively of A–Z, a–z, 0–9, and _. If you need lowercase URL slugs with hyphens, convert the result afterwards — Keyify is intentionally designed for technical keys (case-preserving, underscores).

History

Ouruka keeps a record of every generated ID — so you can always look up which IDs were generated when. Useful for audits, debugging, and completeness checks.

🔧 Under the hood: History is maintained per tenant and accessible via the REST API. It is a convenience / audit feature, not a long-term archive: retention period and maximum row count depend on your package, and older entries are cleaned up automatically. If you need IDs long-term, fetch them regularly and store them in your own system.

Stored per entry:

  • the generated ID (UIDs are stored masked — only the last 5 characters visible),
  • schema name and pattern used,
  • generation timestamp (UTC),
  • source IP.

Retrieval with Pagination

curl "https://ouruka.com/v2/history?limit=50&offset=0" -H "X-API-KEY: $KEY"
[
  { "id": "ORD-2026-000002", "schema_name": "order",
    "pattern_used": "ORD-{DATE:YYYY}-{COUNTER:6}",
    "requested_at": "2026-07-26T10:15:03.906071Z", "source_ip": "203.0.113.7" }
]
  • limit: number of entries (default 50, maximum 200).
  • offset: skip the first N entries — ?limit=50&offset=50 returns the second page.
  • Sorted by: newest first.

🔧 Under the hood: For long lists, combine limit and offset to page through results ("infinite scroll"): page 1 = offset=0, page 2 = offset=50, etc. The source_ip is the real client IP (from X-Forwarded-For behind the reverse proxy); timestamps are microsecond-precise in UTC — convert to local time for display.

Packages & Limits

Every account has a package that defines how much you can use — how many schemas, how many IDs per month, and so on. From free trial to the largest plan, everything is clearly tiered.

🔧 Under the hood: Limits are enforced server-side. Monthly usage is counted in Redis and reset at the start of each month; schema and mapping limits are checked against the current count. When a limit is reached, a clear 403 response is returned with a hint — generation fails in a controlled way instead of silently losing data.

Every tenant belongs to a package with defined quotas.

Package Schemas IDs/month Mapping tables Note
Discovery 2 250 1 14-day trial period
Orbit 5 2,000 1
Galaxy 10 5,000 2
Universe 20 10,000 5

Monthly ID usage is tracked automatically and resets at the start of each month.

Check Your Usage

curl https://ouruka.com/v2/me -H "X-API-KEY: $KEY"
{
  "package": "Orbit",
  "limits": { "max_schemas": 5, "monthly_ids": 2000, "max_mapping_tables": 1, "max_mapping_entries": 50 },
  "usage":  { "schemas": 2, "ids_this_month": 341, "mapping_tables": 1 }
}

What Happens at the Limit?

  • Schema limit reachedPOST /v2/schemas responds with 403 and a hint.
  • Monthly ID limit reachedPOST /v2/generate/... responds with 403.
  • Error responses are JSON, e.g.:
{ "error": "Monthly ID limit reached (2000/2000).",
  "upgrade_hint": "Upgrade to Galaxy for a higher quota." }

Current package details and pricing are available at https://ouruka.com.

Security & Compliance

This chapter describes the technically implemented security measures of Ouruka. It is a factual description of the controls in the source code and operations — not a guarantee of formal certification (e.g. ISO 27001, SOC 2). Where compliance frameworks are mentioned, it refers to how individual requirements are addressed by concrete measures.

Transport Security

All traffic runs over HTTPS/TLS. Unencrypted connections are not served.

Why no "end-to-end encryption" — and why that's the right call here

End-to-end encryption (E2E) is familiar from messengers like Signal: a message is encrypted on the sender's device and only decrypted on the recipient's device — the provider in between sees only ciphertext. This works because a messenger is just a courier: it carries the sealed envelope without ever needing to know the contents.

Ouruka is not a courier, it's a baker: its job is to compute the ID — read the pattern, increment the counter, assemble date and variables. To do that, the service must understand the ingredients and produce the result in plain text. A baker who can't see the ingredients can't bake — and an ID service that can't read the inputs can't build a structured ID. E2E is simply the wrong tool for this kind of service — not out of convenience, but by principle.

What actually matters is what genuinely protects — and that's what is implemented:

Protection What it does At Ouruka
TLS (transport) Protects data in transit between you and the service ✅ always
Credentials hashed Even a database breach yields no usable keys ✅ (SHA-256)
Data minimisation Unnecessary data is never stored in the first place ✅ no password system, UID masking
Deletion on request Data can be fully removed ✅ cascading tenant deletion
Encryption at rest Stolen storage media stays unreadable Infrastructure option

In short: for a computing service like Ouruka, the goal is not "the server must never see anything" but rather "as little as necessary — securely transmitted, securely stored, deletable at any time". That's exactly what we deliver.

Authentication & Credentials

  • API keys are never stored in plain text, only as SHA-256 hashes. The raw key is output exactly once (when created or rotated) and never again. Even read access to the database yields no usable keys.
  • The administrative master key is compared timing-safely (crypto/subtle.ConstantTimeCompare) — preventing stepwise guessing via timing measurements.
  • The master key exists only as an environment variable on the server — never in source code or the repository — and is never written to logs.
  • No default/hardcoded credentials: Without correctly set configuration (DB, Redis, master key), the service does not start. There are no "forgotten" back doors.

Permissions & Abuse Protection

  • Two separate levels: Tenant key (X-API-KEY) for your own data only, master key (X-MASTER-KEY) for administrative operations. Customers cannot self-register.
  • Privilege boundary: The internal unlimited package cannot be assigned via the API (responds 403) — only manually in the backend.
  • Rate limiting on public endpoints (60 requests/minute/IP) against abuse and overload.
  • Key rotation as a response to compromised keys: new key immediately valid, old one immediately invalid — no downtime.

Data Integrity

  • Counters are incremented via an atomic Redis operation — even under massive concurrency, each value is assigned exactly once (load test: 500,000 IDs, 0 duplicates).
  • Schemas are immutable — a running number series cannot be redefined retroactively.
  • Inputs are validated (e.g. allowed UID/hash lengths, patterns, mapping keys).

Privacy by Design

  • Data minimisation: No username/password system; the API key serves as both identity and credential. No unnecessary personal data is collected.
  • Masking: Random UIDs are stored in history only in masked form (last 5 characters visible).
  • Right to deletion: When a tenant is deleted, all associated data (schemas, history, counters, mappings) is cascadingly removed.
  • Timestamps are consistently stored in UTC.

Auditability

  • Every request receives a unique X-Request-ID, which appears in structured JSON logs — the foundation for error analysis, monitoring, and audits.

Mapping to Common Requirements

The measures above address, among others, the following frequently audited principles (without claiming completeness or certification):

Principle Implementation at Ouruka
Confidentiality of credentials Keys hashed only (SHA-256), master key timing-safe, never in code
Encryption in transit HTTPS/TLS
Data minimisation (GDPR Art. 5) No password system, no unnecessary personal data
Right to erasure (GDPR Art. 17) Cascading tenant deletion
Integrity & availability Atomic counters, rate limiting, fail-open on cache failure
Logging / auditability Request IDs + structured logs

FAQ

Are the IDs truly unique?

Yes. Ouruka guarantees uniqueness within a schema (schema IDs) and within a tenant (UIDs). Counters are incremented atomically — even with parallel requests, each counter value is assigned only once. Proven by load tests: 500,000 IDs under full load, 0 duplicates.

What happens if two systems request an ID at the same time?

Ouruka uses an atomic counter operation (Redis Lua script) that eliminates race conditions. Even two requests arriving within the same millisecond receive different, unique IDs.

Can I reset the counter?

There is no separate reset endpoint. For periodic resets, use an interval counter ({COUNTER:N:yearly} etc.) — it resets automatically. A one-time reset is possible by deleting the schema and recreating it (the counter then restarts at start_value). Note: with an unchanged pattern, previously issued values may be reissued — in that case also adjust the pattern (e.g. add a year segment or a fixed prefix).

What is the difference between schema IDs and UIDs?

Schema IDs follow a custom-defined pattern and can carry readable information (date, counter, variables) — ideal for document or product numbers. UIDs are purely random and semantically empty — ideal for technical keys and tokens.

How secure is the API key?

Transmitted only over HTTPS; keys are never stored in plain text (only as SHA-256 hash); comparison runs timing-safely. If you suspect a key has been compromised, rotate it immediately. Always store keys outside of source code.

Can I have multiple schemas?

Yes, depending on your package. Each schema has its own independent counter, history, and pattern — typically one schema per document type (orders, invoices, customers …).

What happens if a variable value is empty?

The placeholder is replaced with an empty string; with separator syntax ({VAR1:-}), the separator is also omitted — no double separators. Date placeholders are never empty (always the system date).

How is the performance?

ID generation typically responds in the double-digit to low triple-digit millisecond range; in load tests, 1,000+ IDs/second were measured at 0 errors and 0 duplicates. For specific SLA requirements, please contact support.

Is Ouruka easy to integrate?

Yes. Standard REST API with JSON — only HTTP requests needed, no SDK dependencies, no proprietary protocols. Every language that can do HTTP is compatible (Python, Java, PHP, JavaScript …), as are low-code platforms like Make or Zapier.

API Reference (Swagger)

The complete, machine-readable API reference is available interactively at:

https://ouruka.com/swagger/

There you will find:

  • All endpoints with methods, paths, and descriptions.
  • Request/response schemas including data types and required fields.
  • Interactive testing directly in the browser — click "Authorize", enter your X-API-KEY, and try out endpoints.
  • Error codes and their meaning.

Endpoint Overview

Method Path Purpose
GET /health System status (public)
GET /v2/changelog Version history (public)
GET /v2/me Tenant info, limits, usage
POST /v2/me/rotate-key Rotate your own API key
POST / GET / DELETE /v2/schemas Manage schemas
POST /v2/generate/{schema} Generate an ID
GET /v2/history History (pagination)
POST /v2/uid/numeric · /v2/uid/alpha Random IDs
POST /v2/keyify Text → key
POST / GET / DELETE /v2/mappings/... Mapping tables

Open-Source Components & Audit

This overview serves as a Software Bill of Materials (SBOM) and as a starting point for an open-source audit. It lists all third-party components that Ouruka is built from or uses at runtime, along with version and licence.

Note on authority: Licence information reflects general knowledge about the respective projects. For a formal audit, an automated licence/SBOM scan should also be run (see Reproducing the SBOM), and the respective LICENSE file should be checked in case of doubt.

The Application Itself

Property Value
Name / module Ouruka ID Service (id-service)
Language / toolchain Go 1.25 (toolchain 1.26), standard library under BSD-3-Clause
Application licence Proprietary (no open-source release) — does not itself trigger OSS obligations
Architecture Monolith (net/http, no framework) + PostgreSQL + Redis, Docker

Go Modules Compiled into the Service Binary

Determined from the built binary (go version -m) — this is the authoritative runtime bill of materials.

Component Version Purpose Licence
github.com/lib/pq v1.11.2 PostgreSQL driver MIT
github.com/redis/go-redis/v9 v9.18.0 Redis client BSD-2-Clause
github.com/cespare/xxhash/v2 v2.3.0 Hash (Redis client dep) MIT
github.com/dgryski/go-rendezvous (pseudo) Hashing (Redis client dep) MIT
go.uber.org/atomic v1.11.0 Atomics MIT
github.com/swaggo/http-swagger v1.3.4 Swagger UI handler MIT
github.com/swaggo/files v1.0.1 Swagger UI assets MIT
github.com/swaggo/swag v1.16.6 Swagger spec MIT
github.com/go-openapi/spec v0.20.6 OpenAPI spec Apache-2.0
github.com/go-openapi/jsonpointer v0.19.5 OpenAPI Apache-2.0
github.com/go-openapi/jsonreference v0.20.0 OpenAPI Apache-2.0
github.com/go-openapi/swag v0.19.15 OpenAPI Apache-2.0
github.com/KyleBanks/depth v1.2.1 Swag dep MIT
github.com/josharian/intern v1.0.0 JSON dep MIT
github.com/mailru/easyjson v0.7.6 JSON MIT
golang.org/x/net v0.34.0 Networking BSD-3-Clause
golang.org/x/mod v0.17.0 Module parsing BSD-3-Clause
golang.org/x/tools (pseudo) Tooling BSD-3-Clause
gopkg.in/yaml.v2 v2.4.0 YAML Apache-2.0

Functionally relevant at runtime are primarily lib/pq and redis/go-redis; the rest serves the embedded /swagger/ endpoint. Development/test only (not in the binary): stretchr/testify, bsm/ginkgo, bsm/gomega, google/go-cmp.

Container Images (Operations)

Image Specific version Role Licence note
golang:1.25-alpine Build (multi-stage) Go: BSD-3; build only, not in the delivery image
alpine:3.21 Runtime base musl (MIT), BusyBox (GPLv2), apk-tools (GPLv2) — base OS, used unmodified
postgres:16-alpine PostgreSQL 16.13 Database PostgreSQL Licence (permissive, BSD/MIT-like)
redis:7-alpine Redis 7.4.8 Cache / counters ⚠️ RSALv2 / SSPLv1 (see Finding below)

Third-Party Systems (Operations)

  • PostgreSQL 16 — persistent storage. Licence: PostgreSQL Licence (permissive).
  • Redis 7.4 — atomic counters and rate-limit buckets. Licence: RSALv2/SSPLv1 (see Finding).
  • Reverse proxy / TLS termination (infrastructure, outside this repo).

Findings for the Auditor

  1. ⚠️ Redis 7.4.8 is no longer OSI open source. From Redis 7.4 onwards, the dual licence RSALv2 / SSPLv1 applies (source-available). For pure in-house use as a dependency, this is generally non-critical, but an OSS audit will flag it as a non-OSS component. Decision: Ouruka deliberately continues to use Redis — permitted and free for in-house use. Should a strict "OSI open source only" requirement ever arise, there are two straightforward alternatives (redis:7.2-alpine, still BSD-3-Clause, or the compatible fork Valkey); neither is currently needed.
  2. The Alpine base includes GPLv2 components (BusyBox, apk-tools). They are used as an unmodified OS base and not linked into the application — standard practice, but worth listing.
  3. All Go modules compiled into the binary are permissive (MIT / BSD / Apache-2.0). No copyleft (GPL/LGPL) in the application binary.
  4. Version pinning: redis:7, postgres:16 follow the major tag (float within major). For an audit, pin to exact versions or image digests.

In Plain Language: Why We Disclose the Redis Point

We could hide this — we deliberately choose not to. Transparency is a quality mark for us, not a risk.

Redis is a ready-made component from another vendor, which Ouruka uses as a fast "counting engine". Every piece of software comes with a licence — the terms of use. Redis had long been the most permissive kind (anyone can do anything). In 2024, the creators changed the terms from version 7.4: now "source-available" — the code remains viewable and free and permitted for in-house use, but officially no longer qualifies as "true open source". The restriction targets large cloud providers who resell Redis as their own paid service — not users like us.

The analogy: A specialised oven in a restaurant whose manufacturer changes the terms from "use it freely" to "use it yes — but no oven-rental business". For cooking, nothing changes; an auditor notes the point regardless.

For us this means: permitted, free, unproblematic in operation. A formal OSS audit will flag the component as "non-standard open source" — but we deliberately continue to use Redis and openly disclose the point here. Should a strict "OSI open source only" requirement ever arise, Valkey (a technically compatible fork) or Redis 7.2 are two straightforward paths — not currently needed.

Reproducing the SBOM

# Runtime bill of materials of the binary (authoritative):
go build -o /tmp/ouruka . && go version -m /tmp/ouruka

# Full module graph:
go list -m all

# Specific service versions in operation:
docker exec ouruka-postgres postgres --version
docker exec ouruka-redis    redis-server --version

Recommended for a formal audit (not installed in this repo): generate a machine-readable SBOM, e.g. with cyclonedx-gomod mod -json (CycloneDX) or syft / trivy for Go and container images, as well as a licence scan with go-licenses report ./....