FlexiMetaDoc API Reference
Versioned, deterministic reference for integrators, security reviewers, and auditors.
Use the onboarding section below as the primary first-read surface for new integrations.
Standard Error Object Schema (All 4xx/5xx Responses)
| Field | Requirement | Type | Notes |
|---|---|---|---|
error.code |
Required | string | Machine-readable stable error code (for example: VALIDATION_FAILED). |
error.message |
Required | string | Human-readable deterministic message. |
error.details |
Optional | array | Defaults to empty array when omitted. |
error.correlationId |
Required | UUID | Echoes X-Correlation-Id for audit traceability. |
error.timestampUtc |
Required | ISO-8601 UTC string | Server time when error was produced. |
{
"error": {
"code": "VALIDATION_FAILED",
"message": "One or more request fields are invalid.",
"details": [
{
"field": "title",
"issue": "Title is required"
}
],
"correlationId": "9f05af26-073d-4c0d-a860-34beea5428f0",
"timestampUtc": "2026-04-09T00:00:00Z"
}
}
Authentication
| Field | Requirement | Details |
|---|---|---|
Authorization |
Required | Use Bearer <jwt_token>. |
| Token algorithm | Required | JWT with RS256 signature; include sub, scope, iat, and exp. |
| Expiry behavior | Required | Expired/invalid token returns 401 with standard error object schema. |
| Insufficient scope | Required | Insufficient scope returns 403 with standard error object schema. |
{
"error": {
"code": "AUTH_TOKEN_EXPIRED",
"message": "The access token is expired.",
"details": [
{
"field": "Authorization",
"issue": "Expired token"
}
],
"correlationId": "9f05af26-073d-4c0d-a860-34beea5428f0",
"timestampUtc": "2026-04-09T00:00:00Z"
}
}
Incoming API onboarding
Deterministic onboarding workflow for inbound API consumers with explicit contract fields, payload examples, and failure handling semantics.
Changelog
- v1.0.1 - Clarified schemas, endpoint contracts, and webhook security controls.
- v1.0.0 - Initial API v1 reference publication.
Register service identity
Provision a traceable service principal before any credential issuance.
Provisioning API field (POST /integrations/services) |
UI action field | Requirement | Deterministic rule |
|---|---|---|---|
tenantId |
Tenant | Required | UUID; must match the operator tenant context. |
serviceName |
Service name | Required | 3-100 chars; unique per tenant. |
environment |
Environment | Required | Enum: dev, test, prod. |
description |
Description | Optional | 0-500 chars; defaults to empty string when omitted. |
contactEmail |
Owner email | Optional | RFC 5322 format; used for lifecycle notifications. |
Minimal payload
{
"tenantId": "7f86bb03-e745-4f95-90b7-0f4f7abff25c",
"serviceName": "erp-sync",
"environment": "prod"
}
Maximal payload
{
"tenantId": "7f86bb03-e745-4f95-90b7-0f4f7abff25c",
"serviceName": "erp-sync",
"environment": "prod",
"description": "Inbound sync from ERP to FlexiMetaDoc",
"contactEmail": "owner@example.com"
}
Issue inbound API credentials
Generate scoped credentials bound to a registered service identity.
Provisioning API field (POST /integrations/services/{serviceId}/keys) |
UI action field | Requirement | Deterministic rule |
|---|---|---|---|
displayName |
Key label | Required | 1-100 chars; immutable for audit traceability. |
scopes |
Scopes | Required | Non-empty string array; least-privilege only. |
expiresAtUtc |
Expiration | Required | ISO-8601 UTC timestamp; must be within 365 days. |
ipAllowList |
Allowed IPs | Optional | CIDR array; empty means any source IP. |
metadata |
Metadata tags | Optional | String map for governance and change-control references. |
Minimal payload
{
"displayName": "erp-sync-primary",
"scopes": ["documents.write"],
"expiresAtUtc": "2026-10-01T00:00:00Z"
}
Maximal payload
{
"displayName": "erp-sync-primary",
"scopes": ["documents.write", "records.write", "records.read"],
"expiresAtUtc": "2026-10-01T00:00:00Z",
"ipAllowList": ["198.51.100.0/24", "203.0.113.10/32"],
"metadata": {
"ownerTeam": "finance-platform",
"changeTicket": "CAB-2045"
}
}
Sign request (X-Integration-Key, timestamp, nonce, signature)
Inbound API calls are accepted only when all signing headers validate within the replay window.
| Header | Requirement | Validation rule |
|---|---|---|
X-Integration-Key |
Required | Matches an active key ID in the caller tenant scope. |
X-Integration-Timestamp |
Required | UTC epoch seconds; max clock skew ±300 seconds. |
X-Integration-Nonce |
Required | UUIDv4; single-use within a 10-minute replay cache. |
X-Integration-Signature |
Required | UPPERCASE_HEX(HMAC-SHA256(canonicalRequestUtf8Bytes, keySecretUtf8Bytes)). Lowercase hex and Base64 are invalid for v1.0. |
canonicalRequest =
HTTP_METHOD + "\n" +
REQUEST_PATH + "\n" +
UPPERCASE_HEX(SHA256(RAW_REQUEST_BODY_BYTES)) + "\n" +
X-Integration-Timestamp + "\n" +
X-Integration-Nonce
X-Integration-Signature = UPPERCASE_HEX(HMAC_SHA256(canonicalRequest, keySecret))
C# signing example (.NET)
using System.Security.Cryptography;
using System.Text;
static string SignRequest(
string httpMethod,
string requestPath,
byte[] rawBodyBytes,
string timestamp,
string nonce,
string keySecret)
{
var bodyHash = SHA256.HashData(rawBodyBytes);
var bodyHashHex = Convert.ToHexString(bodyHash); // uppercase hex
var canonicalRequest =
httpMethod + "\n" +
requestPath + "\n" +
bodyHashHex + "\n" +
timestamp + "\n" +
nonce;
var canonicalBytes = Encoding.UTF8.GetBytes(canonicalRequest);
var secretBytes = Encoding.UTF8.GetBytes(keySecret);
var signature = HMACSHA256.HashData(secretBytes, canonicalBytes);
return Convert.ToHexString(signature); // X-Integration-Signature
}
Deterministic auth diagnostics: malformed signature encoding returns failureCode=signature_invalid_encoding; valid uppercase hex with non-matching digest returns failureCode=signature_mismatch.
Rotate and revoke keys
| Provisioning action | API request | UI action | Required vs optional fields | Deterministic result |
|---|---|---|---|---|
| Rotate key | POST /integrations/services/{serviceId}/keys/{keyId}/rotate |
Generate replacement key | reason Required, effectiveAtUtc Optional |
Existing key enters grace state until cutover; new key is immediately available. |
| Revoke key | POST /integrations/services/{serviceId}/keys/{keyId}/revoke |
Revoke key | reason Required, ticketRef Optional |
Key status changes to revoked and cannot be reactivated. |
Minimal rotate payload
{
"reason": "scheduled-rotation"
}
Maximal rotate payload
{
"reason": "scheduled-rotation",
"effectiveAtUtc": "2026-07-01T00:00:00Z"
}
Minimal revoke payload
{
"reason": "suspected-compromise"
}
Maximal revoke payload
{
"reason": "suspected-compromise",
"ticketRef": "SEC-9081"
}
Failure-mode playbook (deterministic error objects)
All failures below use the same object shape for traceability: error.code, error.message, error.details[], error.correlationId, error.timestampUtc.
Expired key (401)
{
"error": {
"code": "INTEGRATION_KEY_EXPIRED",
"message": "Integration key is expired.",
"details": [{ "field": "X-Integration-Key", "issue": "Expired at 2026-10-01T00:00:00Z" }],
"correlationId": "73ef112f-86bc-48fa-ae95-c7f5f3b7b6c1",
"timestampUtc": "2026-10-01T00:02:10Z"
}
}
Revoked key (401)
{
"error": {
"code": "INTEGRATION_KEY_REVOKED",
"message": "Integration key has been revoked.",
"details": [{ "field": "X-Integration-Key", "issue": "Revoked by admin policy" }],
"correlationId": "f917ec57-9723-49c4-8388-37ef9fafe4f2",
"timestampUtc": "2026-10-01T00:03:50Z"
}
}
Signature mismatch (401)
{
"error": {
"code": "INTEGRATION_SIGNATURE_MISMATCH",
"message": "Request signature validation failed.",
"details": [{ "field": "X-Integration-Signature", "issue": "Computed signature does not match" }],
"correlationId": "e72f6ff5-f6cc-4600-a5e1-502498d67230",
"timestampUtc": "2026-10-01T00:04:12Z"
}
}
Tenant-scope violation (403)
{
"error": {
"code": "INTEGRATION_TENANT_SCOPE_VIOLATION",
"message": "Integration key is not authorized for the target tenant.",
"details": [{ "field": "tenantId", "issue": "Requested tenant does not match key scope" }],
"correlationId": "3526814f-c56f-40dd-8f95-7c5dc82f982a",
"timestampUtc": "2026-10-01T00:05:03Z"
}
}
Security notes (mandatory)
- Apply least-privilege scopes and issue distinct keys per service and environment boundary.
- Store key secrets only in managed secret storage (for example vault or HSM-backed systems); never in source control or plaintext configuration.
- Enforce replay protection using nonce uniqueness checks and strict timestamp skew windows.
- Do not log plaintext keys, secret values, or full signatures; log only key identifiers and correlation IDs.
Conventions
| Convention | Requirement | Definition |
|---|---|---|
| Base URL | Required | https://api.fleximetadoc.example/v1 |
| Content-Type | Required | application/json; charset=utf-8. |
Idempotency-Key |
Required for POST/PATCH writes | UUID, retained for 24 hours; duplicate keys return original outcome. |
| Pagination query params | Optional | page default 1; pageSize default 25, max 100. |
| Rate limits | Required | 120 requests/minute per token. 429 returns Retry-After seconds. |
X-Correlation-Id |
Required | Client-supplied UUID, echoed in responses for end-to-end tracing. |
Endpoint Catalog
Endpoint Group: Documents
POST /documents - Create a document resource.
| Field | Type | Requirement | Behavior |
|---|---|---|---|
title |
string | Required | 1-200 chars. |
ownerId |
UUID | Required | Must reference an existing owner. |
description |
string? | Optional | Nullable. Defaults to null when omitted. |
tags |
string[] | Optional | Defaults to [] when omitted. |
retentionDays |
int? | Optional | Nullable. Defaults to tenant policy when omitted. |
Minimal required payload
{
"title": "Q2 Compliance Summary",
"ownerId": "64f7f01a-cf17-4ec0-af30-79d8aadcce5f"
}
Maximal payload
{
"title": "Q2 Compliance Summary",
"ownerId": "64f7f01a-cf17-4ec0-af30-79d8aadcce5f",
"description": "Regulatory and audit report",
"tags": ["audit", "finance", "q2"],
"retentionDays": 365
}
Endpoint Group: Records
POST /records - Create an indexed record within a document.
| Field | Type | Requirement | Behavior |
|---|---|---|---|
documentId |
UUID | Required | Must reference an existing document. |
recordType |
enum | Required | Allowed values: invoice, note, event. |
effectiveDateUtc |
datetime | Required | ISO-8601 UTC. |
metadata |
object | Optional | Defaults to {} when omitted. |
externalReference |
string? | Optional | Nullable. Defaults to null when omitted. |
Minimal required payload
{
"documentId": "4f6f517f-c0dc-43a9-b250-62e301983dc4",
"recordType": "note",
"effectiveDateUtc": "2026-04-09T00:00:00Z"
}
Maximal payload
{
"documentId": "4f6f517f-c0dc-43a9-b250-62e301983dc4",
"recordType": "invoice",
"effectiveDateUtc": "2026-04-09T00:00:00Z",
"metadata": {
"currency": "USD",
"amount": "1250.00",
"approvedBy": "ops-team"
},
"externalReference": "ERP-PO-11883"
}
Request/Response Examples
Success path (201 Created)
{
"data": {
"id": "de65db84-f852-44c2-9499-0f778530f2ef",
"status": "created",
"createdAtUtc": "2026-04-09T00:00:00Z"
},
"meta": {
"apiVersion": "v1",
"correlationId": "9f05af26-073d-4c0d-a860-34beea5428f0"
}
}
Failure path (400 Bad Request)
{
"error": {
"code": "VALIDATION_FAILED",
"message": "One or more request fields are invalid.",
"details": [
{
"field": "recordType",
"issue": "Value must be one of: invoice, note, event"
}
],
"correlationId": "9f05af26-073d-4c0d-a860-34beea5428f0",
"timestampUtc": "2026-04-09T00:00:00Z"
}
}
Webhooks
- Delivery: HTTPS
POSTwith JSON body. - Signature header:
X-FMD-Signatureformatsha256=<hex_digest>. - Timestamp header:
X-FMD-TimestampUNIX seconds. - Replay protection: Reject payloads older than 300 seconds and deduplicate event IDs for 24 hours.
- Retry behavior: Non-2xx responses trigger retries with exponential backoff.
Signature verification pseudocode
signedPayload = X-FMD-Timestamp + "." + rawBody
expected = HMAC_SHA256(webhookSecret, signedPayload)
if !constantTimeEquals(expected, X-FMD-Signature): reject(401)
if abs(currentUnixSeconds - X-FMD-Timestamp) > 300: reject(401)
if eventIdAlreadyProcessed: reject(409)
acknowledgeWith2xx()
Security Expectations
- Credential handling: Keep credentials in a managed secret store; never commit or log secrets.
- Least privilege scopes: Request only required scopes and rotate tokens on a defined schedule.
- Retry and backoff: Use exponential backoff with jitter for
429/5xx; cap at 5 retry attempts. - Input validation: Validate UUIDs, enum values, timestamps, and string lengths before request submission.
- Deterministic failures: Handle non-success responses through the standard error schema and preserve correlation IDs.