API Documentation

Comprehensive reference for the DMART API. All endpoints, data structures, and usage examples.

Base URL & Authentication

The API is served by the self-contained DMART binary (.NET 10 Native AOT) on ASP.NET Core / Kestrel. The base URL for all API requests is typically: http://localhost:8282 (or as configured via LISTENING_HOST / LISTENING_PORT).

Most endpoints require authentication with a JWT bearer token (HS256), supplied either as a header or a cookie:

Header Authorization: Bearer <your_token>

Cookie auth_token=<your_token>

Tokens are obtained via /user/login (password or OTP) or through OAuth social login (Google, Facebook, Apple). DMART also exposes a full OAuth 2.1 Authorization Server — discovery, Dynamic Client Registration, and authorize/token endpoints under /.well-known — used for automatic onboarding of MCP clients.

Interactive API documentation is available as Swagger UI at /docs, with the raw OpenAPI schema at /docs/openapi.json (both served by ASP.NET Core).

Common Data Structures

Record

Represents a resource in the system. This is the primary object for creating or updating entities.

{
  "resource_type": "content",
  "shortname": "my-article",
  "subpath": "/blog",
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "attributes": {
    "is_active": true,
    "slug": "my-article-slug",
    "displayname": { "en": "My Article" },
    "description": { "en": "A description" },
    "tags": ["news", "tech"],
    "owner_shortname": "jdoe",
    "owner_group_shortname": "editors",
    "created_at": "2023-10-01T12:00:00",
    "updated_at": "2023-10-01T12:00:00",
    "payload": {
      "content_type": "json",
      "schema_shortname": "article",
      "body": { "title": "Hello World", "content": "..." }
    },
    "acl": [
      {
        "user_shortname": "jane",
        "allowed_actions": ["view", "update"]
      }
    ],
    "relationships": [
      {
        "related_to": {
          "space_name": "data",
          "type": "user",
          "subpath": "users",
          "shortname": "jane"
        },
        "attributes": { "role": "editor" }
      }
    ]
  }
}

Request

Used for batch operations (create, update, delete, etc.).

{
  "space_name": "data",
  "request_type": "create",
  "records": [ ... list of Record objects ... ]
}

Query

Used for searching and filtering resources. Supports full-text search, aggregation, joins, and JQ filters.

{
  "type": "search",
  "space_name": "data",
  "subpath": "/blog",
  "exact_subpath": false,
  "filter_types": ["content"],
  "filter_schema_names": ["article"],
  "filter_shortnames": [],
  "filter_tags": ["tech"],
  "search": "@title:Hello*",
  "from_date": "2023-01-01T00:00:00",
  "to_date": "2023-12-31T23:59:59",
  "exclude_fields": ["payload.body"],
  "include_fields": ["shortname", "displayname"],
  "highlight_fields": { "description.en": "" },
  "sort_by": "created_at",
  "sort_type": "descending",
  "retrieve_json_payload": true,
  "retrieve_attachments": false,
  "retrieve_total": true,
  "validate_schema": true,
  "retrieve_lock_status": false,
  "jq_filter": ". | select(.attributes.is_active == true)",
  "limit": 10,
  "offset": 0,
  "aggregation_data": {
    "group_by": ["@tags"],
    "reducers": [
      { "reducer_name": "count_distinct", "alias": "count", "args": ["@shortname"] }
    ]
  },
  "join": [
    {
      "join_on": "uuid",
      "alias": "author_details",
      "query": { "...nested Query..." }
    }
  ]
}

Enums & Possible Values

Enum Values
ResourceType (30) user, group, folder, schema, content, log, acl, comment, media, data_asset, locator, relationship, alteration, history, space, permission, role, ticket, json, lock, post, reaction, reply, share, plugin_wrapper, notification, csv, jsonl, sqlite, parquet
RequestType (7) create, update, patch, update_acl, assign, delete, move
ContentType (21) text, comment, reaction, markdown, html, json, image, image_jpeg, image_png, image_svg, image_gif, image_webp, python, pdf, audio, video, csv, parquet, jsonl, apk, sqlite
ActionType (11) query, view, update, create, delete, attach, assign, move, progress_ticket, lock, unlock
QueryType (12) search, subpath, events, history, tags, random, spaces, counters, reports, aggregation, attachments, attachments_aggregation
Language (5) arabic, english, kurdish, french, turkish These full spellings are the persisted enum wire values (e.g. space.languages). The 2-letter keys ar / en / ku / fr / tr are ONLY keys inside displayname / description translation maps — they are NOT Language enum values.
SortType (2) ascending, descending
UserType (3) web, mobile, bot
PluginType (2) hook, api
EventListenTime (2) before, after
JoinType (4) left, right, inner, outer
PublicSubmitResourceType (2) content, ticket The only two resource types accepted by /public/submit.
Status (2) success, failed The response-envelope status field (see below).
TaskType (1) query

String conventions (NOT typed enums)

The following values are string conventions — they are validated/handled as plain strings and have no backing C# enum. Only the two permission condition constants (own, is_active) are real string constants; reaction kinds, the lock action string, and notification type/priority are inherited Python-era conventions.

Convention Values
Permission conditions (real string constants) own, is_active
Reaction kinds (convention) like, dislike, love, care, laughing, sad
Lock action (convention) lock, unlock (acquire/release via the /managed/lock routes)
Notification type (convention) admin, system
Notification priority (convention) high, medium, low

Response envelope

Every /managed, /public, and /user endpoint returns the same envelope. A success carries records and an attributes block (with total / returned counts on queries); a failure carries a single error triple and records: null.

// success
{
  "status": "success",
  "error": null,
  "records": [ /* ...Record objects... */ ],
  "attributes": { "total": 128, "returned": 10 }
}

// failure
{
  "status": "failed",
  "error": {
    "type": "jwtauth",
    "code": 401,
    "message": "Not authenticated",
    "info": null
  }
}

User Management /user

GET /user/check-existing

Checks if a user with specific fields already exists.

Query Params: shortname, msisdn, email (all optional)

POST /user/create

Registers a new user.

{
  "resource_type": "user",
  "shortname": "jdoe",
  "subpath": "users",
  "attributes": {
    "email": "jdoe@example.com",
    "password": "StrongPassword123!",
    "displayname": { "en": "John Doe" }
  }
}

POST /user/login

Authenticates a user and returns a token.

{
  "shortname": "jdoe",
  "password": "StrongPassword123!"
}

Or via OTP:

{
  "msisdn": "1234567890",
  "otp": "123456"
}

GET /user/profile

Retrieves the profile of the currently logged-in user.

POST /user/profile

Updates the profile of the currently logged-in user.

POST /user/logout

Logs out the current user.

POST /user/delete

Deletes the current user's account.

POST /user/otp-request

Requests an OTP for login or verification.

POST /user/otp-request-login

Requests an OTP specifically for login purposes.

POST /user/password-reset-request

Initiates the password reset process.

POST /user/otp-confirm

Verifies the OTP sent to the user.

POST /user/reset

Resets a user's password (requires appropriate permissions).

POST /user/validate_password

Checks if the provided password is correct for the current user.

Social Login Callbacks

Handles callbacks from social login providers.

GET /user/google/callback

Google OAuth callback.

GET /user/facebook/callback

Facebook OAuth callback.

GET /user/apple/callback

Apple OAuth callback.

Managed Content /managed

Endpoints for authenticated management of content and resources.

POST /managed/request

Performs batch operations (create, update, delete, etc.).

{
  "space_name": "data",
  "request_type": "create",
  "records": [
    {
      "resource_type": "content",
      "shortname": "new-item",
      "subpath": "/items",
      "attributes": {
        "displayname": {"en": "New Item"},
        "payload": {
          "content_type": "json",
          "body": {"key": "value"}
        }
      }
    }
  ]
}

Delete with force / dry_run: a delete request accepts two top-level flags. force: true cascade-deletes a non-empty folder (and all its contents); a plain delete of a non-empty folder is rejected. dry_run: true projects the full cascade and returns an affected-count report without removing anything.

{
  "space_name": "data",
  "request_type": "delete",
  "force": true,
  "dry_run": true,
  "records": [
    { "resource_type": "folder", "shortname": "archive", "subpath": "/" }
  ]
}
// -> success envelope; each record's attributes carry a per-category
//    "report" of what WOULD be deleted, plus "dry_run": true (nothing removed).

POST /managed/query

Executes a query against the database.

{
  "type": "search",
  "space_name": "data",
  "subpath": "/content",
  "search": "*",
  "limit": 5
}

POST /managed/semantic-search

Natural-language (vector) search — embeds the query and returns the top-N most similar entries by cosine distance over entries.embedding. Requires the pgvector extension and a configured embedding provider (EMBEDDING_API_URL); when either is missing it returns a clean 400 failure envelope. Results are permission-filtered, and limit is clamped to a max of 100.

// request
{
  "query": "how do refunds work",
  "space_name": "data",        // optional
  "subpath": "/articles",       // optional prefix
  "resource_types": ["content"],// optional
  "limit": 10                    // optional (default 10, max 100)
}

// success -> each record's attributes carry space_name, similarity (0..1), uri
{
  "status": "success",
  "records": [
    {
      "resource_type": "content",
      "shortname": "refund-policy",
      "subpath": "/articles",
      "attributes": { "space_name": "data", "similarity": 0.83,
                      "uri": "dmart://data/articles/refund-policy" }
    }
  ],
  "attributes": { "returned": 1, "matched": 3 }
}

// not configured
{ "status": "failed",
  "error": { "type": "request", "code": 400,
             "message": "semantic search not configured — set EMBEDDING_API_URL ..." } }

POST /managed/reindex-embeddings

Admin tool that (re)embeds entries into the pgvector column — used to backfill or rebuild the semantic-search index. Runs as a background job.

GET /managed/reindex-embeddings/status

Admin-only. Returns the live progress of the current or last re-index run.

POST /managed/import

Imports data from a ZIP file. Body: multipart/form-data with zip_file.

POST /managed/export

Exports data based on a Query object.

POST /managed/csv

Exports query results as CSV.

PUT /managed/progress-ticket/{space}/{subpath}/{shortname}/{action}

Updates the state of a ticket workflow.

{
  "resolution": "Fixed",
  "comment": "Done"
}

GET /managed/payload/{resource_type}/{space}/{subpath}/{shortname}.{ext}

Gets the raw payload of a resource.

POST /managed/resource_with_payload

Uploads a file as payload. Body: multipart/form-data with payload_file, request_record, space_name.

POST /managed/resources_from_csv/{resource_type}/{space}/{subpath}/{schema}

Creates resources from a CSV file.

GET /managed/entry/{resource_type}/{space}/{subpath}/{shortname}

Gets the metadata of a resource.

GET /managed/byuuid/{uuid}

Gets an entry by UUID.

GET /managed/byslug/{slug}

Gets an entry by slug.

GET /managed/health/{health_type}/{space}

Runs a health check. Types: soft, hard.

PUT /managed/lock/{resource_type}/{space}/{subpath}/{shortname}

Acquires an entry lock for the caller. While a lock is held, update and delete from any user other than the lock holder are rejected; the holder may re-lock (extend) their own lock. Query results can surface the current holder via retrieve_lock_status (see the Query shape above).

DELETE /managed/lock/{space}/{subpath}/{shortname}

Releases the lock the caller holds on an entry.

GET /managed/reload-security-data

Reloads the in-process permissions and roles cache from PostgreSQL.

POST /managed/execute/{task_type}/{space}

Runs a saved query task. The only defined task_type is query: DMART loads a saved query entry by shortname, parses its payload.body as a Query, and executes it. query_overrides merge into the loaded query, and any $param placeholders inside the query's search string (e.g. @status:$state) are substituted from the overrides; unresolved @field:$param fragments are stripped before execution.

The misspelled legacy path /managed/excute/{task_type}/{space} is also mapped for client compatibility.

{
  "shortname": "open-tickets",
  "subpath": "/tasks",
  "query_overrides": { "state": "open", "limit": 20 }
}

POST /managed/apply-alteration/{space}/{alteration_name}

Applies a recorded alteration to an entry.

GET /managed/shortening/{space}/{**rest}

Creates a short link for a DMART resource URL and returns a random token. The resolvable short_url is bounded by APP_URL and the token expires after URL_SHORTER_EXPIRES seconds.

// -> { "short_url": "https://app.example.com/managed/s/aB3xY9" }

GET /managed/s/{token}

Resolves a short token and issues a 302 redirect to its original URL (anonymous-accessible, rate-limited).

Public Access /public

Endpoints for unauthenticated or public access (if configured).

POST /public/query

Executes a query publicly.

GET /public/query/{type}/{space}/{subpath}

Public query via URL params.

GET /public/entry/{resource_type}/{space}/{subpath}/{shortname}

Retrieves a public entry.

GET /public/payload/{resource_type}/{space}/{subpath}/{shortname}.{ext}

Retrieves a public payload.

POST /public/submit/{space}/{**rest}

Submits data to a public endpoint (e.g. a form). An optional leading path segment selects the resource type: it is parsed against PublicSubmitResourceType, so only content or ticket are honored. When the leading segment is not one of those, it is treated as the space name and the resource type defaults to content (it is not rejected). Submitting a ticket additionally requires a workflow shortname in the path.

POST /public/attach/{space}

Attaches a file to a record publicly.

POST /public/excute/{task_type}/{space}

Executes a task publicly.

GET /public/byuuid/{uuid}

Gets a public entry by UUID.

GET /public/byslug/{slug}

Gets a public entry by slug.

QR Codes /qr

Note: the /qr generate/validate endpoints are currently minimal / a stub in this port and are not yet fully featured.

GET /qr/generate/{resource_type}/{space}/{subpath}/{shortname}

Generates a QR code for a resource.

POST /qr/validate

Validates a scanned QR code.

{
  "resource_type": "user",
  "space_name": "data",
  "subpath": "/users",
  "shortname": "jdoe",
  "qr_data": ""
}

System Info /info

GET /info/me

Gets current user info.

GET /info/settings

Gets system settings (restricted to 'dmart' user).

GET /info/manifest

Returns system version and status.

Realtime & WebSockets /ws

DMART pushes live updates over WebSocket connections. A built-in notifier plugin broadcasts entry changes to subscribed clients.

GET /ws

Opens an authenticated WebSocket connection for realtime notifications. The JWT is passed as a query param (ws://host/ws?token=JWT) or via the auth_token cookie; the socket is closed with 401 if the token is invalid, the user is inactive, or the session has been revoked.

After connecting, the client subscribes by sending a notification_subscription message. DMART builds a channel name of the form space:subpath:schema:action:state, defaulting any omitted segment to the __ALL__ wildcard. The realtime notifier plugin then broadcasts every matching CRUD event to subscribed clients.

// client -> server: subscribe (omit fields to wildcard them)
{
  "type": "notification_subscription",
  "space_name": "data",
  "subpath": "/tickets",
  "schema_shortname": "ticket",   // optional -> __ALL__
  "action_type": "update",         // optional -> __ALL__
  "ticket_state": "open"           // optional -> __ALL__
}
// -> channel "data:/tickets:ticket:update:open"

// server -> client on connect
{ "type": "connection_response", "message": { "status": "success" } }

POST /send-message/{user}

Sends a message to a specific connected user.

POST /broadcast-to-channels

Broadcasts a message to subscribers of one or more channels.

GET /ws-info

Returns information about active WebSocket connections.

Model Context Protocol /mcp

DMART ships a built-in MCP server (Streamable HTTP transport, spec 2025-03-26) so AI agents can query and mutate entries as tools. It is off by default — set ENABLE_MCP=true to expose it; otherwise /mcp and the OAuth 2.1 endpoints are unmapped and answer INVALID_ROUTE (HTTP 422). All requests are authenticated — the caller's JWT flows through to each tool handler. Sessions are tracked via the Mcp-Session-Id header. Pair it with the OAuth 2.1 Authorization Server for automatic client onboarding.

POST /mcp

Sends a JSON-RPC MCP request (initialize, list/call tools, etc.).

GET /mcp

Opens the server-sent-events (SSE) stream for the MCP session.

DELETE /mcp

Terminates the current MCP session.

Available tools (11)

The MCP server exposes these tools to agents. Every tool runs under the caller's JWT, so DMART's permission model is enforced identically to the HTTP API — there is no admin escape hatch.

Tool Purpose
dmart_me Return the authenticated caller's profile.
dmart_spaces List the spaces the caller can access.
dmart_query Search / list entries. Results are hard-capped at 50 records per call.
dmart_read Read a single entry (metadata + payload).
dmart_schema Fetch a schema definition for a space/subpath.
dmart_create Create a new entry.
dmart_update Update / patch an existing entry.
dmart_delete Delete an entry. Requires an interactive elicitation confirmation before it proceeds; folders take an optional force flag to cascade-delete non-empty contents.
dmart_history Retrieve the change history of an entry.
dmart_download Download the raw payload / attachment of an entry.
dmart_semantic_search Natural-language vector search (requires pgvector + an embedding provider, same as /managed/semantic-search).