Color scheme: Dark
You are here: start » en » Documentation » Data integration and data access » WKL Database Gateway » Reference » HTTP: administration (reference)

HTTP: administration (reference)

The administration API under /admin/v1/ offers the administration store as OData sets and commands; the administration app for Access and the VBA class WklGatewayClient are built on it.

Syntax

With an administration store, the service offers its tables under /admin/v1/: as OData sets that an administration program links like tables—Access via this service's ODBC driver, with the forms it already has for tables—and as commands for anything that isn't a change to a row.

FormAddressPurpose
ODataGET /admin/v1/odata/, $metadata, {Menge}, {Menge}({Schlüssel}), {Menge}/$countreading with $select, $filter, $orderby, $top, $skip, $count
ODataPOST /admin/v1/odata/{Menge}, PATCH and DELETE on {Menge}({Schlüssel})create (201), update (200, If-Match with the version read), delete (204)
CommandPOST /admin/v1/issue-keyissue a personal key: once in the body, as a one-time link or as a one-time link by email
CommandPOST /admin/v1/activate-keyactivate an on-demand key for a period (id, hours)
CommandPOST /admin/v1/deactivate-keyend the activation of an on-demand key immediately (id)
CommandPOST /admin/v1/save-sign-in-rulecreate a sign-in rule with all networks, people and groups in one change, or replace it as a whole (id)
CommandPOST /admin/v1/explain-sign-inwhat the sign-in policy says for a person, per sign-in method and origin, with the triggering rules
CommandPOST /admin/v1/sign-in-profilewhat is usual for a person when signing in, over a chosen period (sign-in profile)
CommandPOST /admin/v1/test-connectionopen a data source; response: reachable, duration, version, database, login or the server's message with the error number and kind of failure (connection test and service account)
CommandGET /admin/v1/service-accountthe account under which the service signs in to databases with Windows authentication (connection test and service account)
CommandPOST /admin/v1/reload-catalogdiscard the resource catalog of one (dataSource) or all data sources; the next request reads it again
CommandGET /admin/v1/check-storecheck the store as a reload would, without changing anything
CommandPOST /admin/v1/backup-storeone checked copy each of the store and its log file into the sicherungen folder next to it, named after the time (gateway-YYYYMMDD-hhmmss.db and gateway-YYYYMMDD-hhmmss-logs.db); response file, schemaVersion, bytes, logFile, logBytes (the last two empty without a log file). The service chooses the location; a request doesn't name a path. Two backups in the same second: the second gets 409. The copies contain no master key
CommandPOST /admin/v1/reseal-storelike the command of the same name on the computer
CommandPOST /admin/v1/enroll-totpset up a person's second factor (person, delivery: key, link or mail); ends their sessions, with mail only on pickup. The one-time link page shows the address for the authenticator app as a QR code (SVG in the page, nothing is loaded externally) and next to it the key for manual entry
CommandPOST /admin/v1/reset-totpremove a person's second factor and end their sessions
CommandPOST /admin/v1/set-geoip-sourcechoose the source of the geolocation database
CommandPOST /admin/v1/update-geoipdownload the geolocation database from the chosen source now (the server needs internet access)
CommandPOST /admin/v1/upload-geoipaccept a geolocation database file brought by the caller; body = file
CommandPOST /admin/v1/export-loadthe load log as a file for the manufacturer, without persons, origins, or values
CommandGET /admin/v1/update-databasesdatabases of the update module with their settings, plus configurations left out
CommandPOST /admin/v1/update-statusstate of one database: open packages with their deadline, blocking and unknown sessions, drift (database updates)
CommandPOST /admin/v1/update-historyfinished packages of one database with their steps, newest first
CommandPOST /admin/v1/confirm-update, dismiss-update, retry-updateconfirm a waiting package; dismiss a failed or refused one, or apply it again
CommandPOST /admin/v1/move-update-deadline, cancel-update-noticemove the deadline of an announced package; cancel the announcement
CommandPOST /admin/v1/upload-updateput a package into a database's inbox and accept it right away; body = file
PageGET/POST /admin/v1/pickup/{Kennung}one-time link; without sign-in, the ID is the proof

Parameters

Why two forms. Whatever is a list is a set: an administration program needs no code of its own for it. Whatever returns a secret or has an effect beyond the row is a command: a set that Access reads again after creating a row would either have to hand out the key just issued once more, or lose it.

Ready-made clients. The “Overview of WKL Database Gateway Manager and the VBA class” (documentation of the administration app) covers all sets and commands (sign-in with a key or Windows account and a second factor); “WklGatewayClient: VBA class for the data service” (documentation of the administration application) calls the commands from your own VBA applications.

Sets

Sets. They follow the store's tables one to one; lists belonging to a rule or a key are rows of a subset with the key of the parent row.

SetKeyRemark
DataSourcesNameName follows the naming rule for the alias. The connection comes either as the string Connection—write-only, always reads as empty—or as connection properties ConnectionProperties, from which the provider builds the string; one of the two on create, on update a value replaces the sealed string, empty leaves it in place. Provider, ProviderVersion, and ProviderOptions (JSON object of texts) as in the configuration. Switches AllowPassThrough, AllowCheckedStatements (default true), AllowSqlLogins as in the configuration
Providers, ProviderPropertiesId, PackageVersion; Provider, PackageVersion, Scope, Name, LanguageRead-only: the installed provider packages and the properties each describes for its connection; see connection properties of a provider
DataSourceTablesDataSource, Setting, PatternSetting is row_stamp or keyless_write; create and delete only
ImpersonationDenyRules, ImpersonationDenyItemsId; RuleId, Item, ValueItem: login, role, from, except_from
ClientRules, ClientRuleItemsId; RuleId, Item, ValueItem: database, resource, verb, object, except_object (rights per object; the check rejects an entry in the wrong form with 422). Holders counts who has the rule (people, Windows accounts and groups, SQL Server logins). RuleVersion (read-only) is the version that AuditEntries.AccessRule names; if it differs from the version in an entry, the rule has changed since
WindowsAccounts, SqlLoginsAccount, Client; Login, Clientassignments to rules
Persons, PersonRulesId; PersonId, ClientSqlLogin (the person's own SQL Server login, for Impersonate with a personal key), WindowsAccount (name or SID, resolved on write), Email (an address without a display name, suggestion for an invitation mail; otherwise 400), DefaultRole (default identity), SecondFactor (none, pending, active), LeftAt (when the person left the organization, set by the administration, also in advance). Read-only: LastSignInAt and LastSignInMethod, the last successful sign-in (method as in the sign-in log, such as personalKey, windows); the service writes it once a minute, it outlasts the retention of the sign-in log and doesn't change the person's version. For display: Status/StatusKind, SecondFactorText/SecondFactorKind, counts Rules, ValidKeys, OpenSessions, ExpiringKeys (valid keys that expire within 14 days); PersonRules.Person names the person. The service refuses DELETE of a person who has left traces (keys, sessions, sign-ins, load, or their own changes) with 422; disable them with Enabled = false (Retention and deletion)
PersonalKeys, PersonalKeyItemsId; KeyId, Item, Valueissued only with issue-key. Changeable: Label, ReadOnly, TotpExempt (only for a read-only key), ExpiresAt (not beyond the maximum lifetime from issue), OnDemand and Revoked—a revocation can't be lifted. ActiveUntil is set only by activate-key/deactivate-key. For display: Status (“gültig” (valid), “läuft in 5 Tagen ab” (expires in 5 days), “abgelaufen” (expired), “gesperrt” (revoked), “ruht” (dormant), “frei bis 15:30 UTC” (active until 15:30 UTC)), StatusKind, ExpiresInDays, CanRevoke, CanActivate
SignInFileRulesPositionRead-only. Sign-in rules that are still in the configuration file (Gateway:Security:SignInRules). With an administration store they are not in force: they only served as the starting value when the store was created, and the service warns about them at startup. Fields: Position (n as in file:n), Scope, Method, Effect, Mode, Label, Conditions (the lists in use as text, such as ExceptFrom: 10.0.0.0/8; ExceptHours: Mon-Fri 06:00-20:00), Exceptions (count), RuleVersion, InForce (always false with a store). To clean up, remove the entries from the file
SignInRules, SignInRuleItemsId; RuleId, Item, Valuesign-in policy; Item: from, except_from, person, except_person (value: Id of the person), windows_group, except_windows_group (name or SID), country, except_country (country code), operator, except_operator (network operator, AS3320), client, except_client (kind of program), hours, except_hours (time window). A rule's RuleVersion (read-only) is the version sign-in events and explain-sign-in name; if it differs from the version in an event, the rule has changed since. A rule's Mode: Enforce (default) or Report (Try a rule). The check rejects a person who doesn't exist or an invalid country code, network operator or time window with 422
SignInRuleExceptions, SignInRuleExceptionItemsId; ExceptionId, Item, Valueexceptions with an end of a rule: RuleId, ValidUntil (UTC, empty = no end), Label, LapseLogged (read-only: the end is in the change log); Item: person (value: Id of the person), windows_group, country, from, operator, client, hours. The check rejects an exception without attributes and without an end with 422. Moving ValidUntil resets LapseLogged
SettingsNametotp_session_hours, self_service_key_days (self-service), totp_enrol_hours, max_activation_hours (on-demand keys), installation_name (takes precedence over Gateway:Store:TotpIssuer, see configuration), mail_link_hours (validity of a one-time link by email, 1–720 hours), audit_days (retention of the access log, 1–3650 days, takes precedence over Gateway:AuditLog:Days), log_max_megabytes (size of the log file, 64–65,536 MB, takes precedence over Gateway:Store:LogMaxMegabytes; applies without a restart, and the next trim removes the oldest entries first); any other name is rejected, an invalid value with 422
MailTemplatesIdHTML templates for invitation mails, the same for all administrators: Name, Purpose (KeyPickup, TotpEnrollment, Welcome), Subject, Html; Builtin and ChangedAt are set by the service. Placeholders only from {{Person}}, {{Link}}, {{GueltigBis}}, {{Dienst}}, {{Absender}}, {{Organisation}}; KeyPickup and TotpEnrollment need {{Link}}; at most 256 KB. A violation: 400 with all reasons. The three built-in templates (one per purpose) can be replaced but not renamed (400) or deleted (422). A template embeds images as src="cid:brand" (the partner's logo) and src="cid:logo" (the mark of The White Knight Labs); the client attaches both with these IDs. The service doesn't send any mail
SessionTokensIdsecond-factor sessions; only Revoked (final) and delete; Role names the bound identity. For display: Status (“offen” (open), “abgelaufen” (expired), “beendet” (ended)), StatusKind, CanEnd. EndReason names how a session ended: ended (ended in the administration), person-locked (person locked), key-revoked (the session's key revoked), factor-renewed (second factor set up again), factor-reset (second factor removed), expired (expired without being ended before); EndedBy names who ended it, as the change log names them (Konsole for the revoke-key command). The service ends sessions instead of deleting them; sessions ended before this version have both fields empty
DatabaseIdentities, IdentityAssignmentsId; Iddatabase identities. Kind: Login, Own, Alias; the service checks a shared login on save with a trial switch. An assignment names exactly one source: PersonId, WindowsAccount or KeyId; the service adds Source, DataSource and Role
DataSourceObjectsDataSource, Schema, Nameread-only. The objects in the catalog of every enabled data source, written the way a rule names them: Kind is the type under Resources (tables, views, functions, procedures), Pattern the entry under Objects (schema.name). Suggestions for an administration program; a data source whose catalog can't currently be read is missing from the list
UpdateSettingsDatabaseOperating settings of database updates per database: Expand and Contract (automatic or confirm), NoticeMinutes (0–10080), SessionWindowMinutes (1–1440), the channels NotifyEventLog (on by default), NotifyFile and NotifyEmail (off by default, see reports) with the recipients NotifyEmailTo (mail addresses separated by ;; NotifyEmail without a recipient is refused with 400), backup retention BackupKeepCount (1–1000, default 10) and BackupKeepDays (0–3650, default 30); the service sets ChangedAt. A field left out gets its default; a value outside these ranges is refused with 422. Without an entry, and after it is deleted, the defaults apply
SignInHours, SignInOrigins, SignInEvents, SignInFindings, SignInAdvice, GeoIpFiles, GeoIpSourceread-only; see Sign-in log and geolocation database
LoadShapes, LoadHours, LoadPersons, SlowRequests, LoadFindings, LoadAdviceread-only; see Load log
ChangeLogIdread-only
AuditEntriesSequenceread-only. The audit stream (`Gateway:AuditLog`): accesses (Kind = Zugriff: time, caller, alias, resource, method, result, status, correlation) and custom statements (Anweisung, with text). With an administration store, read from the log file, also after a restart, for Gateway:AuditLog:Days; Sequence is then the number in the file. Per query, the set returns at most the newest 20,000 entries that match the period (At), the person (Person eq) and, with Reason ne null, “refusals only” of the filter. Without a store, the last 10,000 entries since start, with statements truncated to 4,000 characters. Person and KeyId name the person and personal key of a call, so that you can filter by them. For a denial by a sign-in rule, SignInRule names that rule's identifier and version (2@8da4235bc439aac0, see Grounds of the decision). Target names what the request addressed: path and query, readable (/odata/lager/dbo/Artikel(17), /odata/lager/dbo/Artikel?$filter=Nr eq 17), at most 2,000 characters, without $skiptoken; a query sent in the request body isn't included. For an answered request to a table or view, AccessRule names the access rule that allowed it: the rule's name and version (lager-lesen@3f2a8c41d07b9e65, the RuleVersion from ClientRules); the entry itself names the alias. Entries from before this extension have both fields empty
ServiceStatusIdread-only, one row: load of the service since startup, from memory—RequestsLastMinute, RequestsLastFiveMinutes, InFlight, MedianMilliseconds, P95Milliseconds, DeniedLastFiveMinutes (401/403/429; without the answer to a request without credentials, with which every Windows sign-in starts), ErrorsLastFiveMinutes (≥ 500), Saturated (more than 50,000 requests in five minutes: numbers are a lower bound), the request limit (RateLimitPerCaller, RateLimitWindowSeconds, empty without a limit) and from the store OpenSessions, ValidKeys, ExpiringKeys (14 days). How far back the logs reach: AuditDays (retention of the access log in days, Gateway:AuditLog:Days; empty without a store or with the log switched off), AuditOldestAt (oldest access kept, empty without an entry), SignInEventDays (Gateway:SignInLog:EventDays), and LogMaxMegabytes (maximum size of the log file; empty without a store). Without the probes under /health

Display fields. Status is a text for display, StatusKind one of five kinds (ok, warn, bad, info, neutral) by which an administration program colors the label—the text is always shown with it, never the color alone. Can… fields say whether a row command is currently possible. The service computes them on read according to the store's clock; they're read-only (writing gives 400) and aren't part of the row version—a key doesn't change just because a day passes. They're delivered rather than computed in Access, because an expression such as IIf(…) in an Access query isn't translated through the ODBC driver, and a data grid in server mode would then fall back to slow reading through Access.

Every set has the property Version (eight bytes of a SHA-256 over the stored values of the row, including sealed ones) as its row version. If you change a row that someone else has changed since you read it, you get 412—in Access, the familiar write conflict message. Values a set shows from another set aren't part of the row version: the number Rules of a person, Holders of an access rule, the name Person of an assignment, a key or a session. A new rule for a person or the person's new name therefore doesn't turn an edit that's open at the time into a write conflict. Numbers are int (Edm.Int32), not bigint: without its option for large numbers, Access shows bigint as text and then rejects a condition such as Id = 3. The filter compares text case-insensitively, as SQL Server does and as the store compares names.

Counting, highest value and grouping

The administration interface reads rows and counts them; it doesn't group. It rejects $apply with 400, and its schema document announces no grouping (Aggregation.ApplySupported is absent). The ODBC driver uses that announcement to decide what it sends to the administration interface (the translation in detail: “Supported SQL (reference)” in the documentation of the ODBC driver):

Statement Access passes to the driverRequest to the administration interfaceResult
SELECT COUNT(*) FROM "Set", for example from DCount("*", "Set")Set/$countNumber of rows
SELECT COUNT(*) FROM "Set" WHERE …, for example from DCount("*", "Set", "Id > 4711")Set/$count?$filter=…Number of matching rows
SELECT … FROM "Set" WHERE … ORDER BY …Set?$select=…&$filter=…&$orderby=…Rows
MAX, MIN, SUM, AVG, COUNT(column), GROUP BY, HAVING, DISTINCT, for example from DMax, DMin, DSum, DAvg, DCount("column", …) or a totals queryNoneThe driver rejects the statement with 42000 (“Der Dienst kündigt die Gruppierung über $apply … nicht an”: the service doesn't announce grouping); Access reports “ODBC–call failed” (3146)

To count, use DCount("*", …), not DCount("column", …): only COUNT(*) becomes a count request, with or without a condition.

To get the highest value of a column, read the first row of a query sorted in descending order. Over HTTP, for example through an administration program's own HTTP access: GET /admin/v1/odata/SignInFindings?$select=Id&$orderby=Id desc&$top=1. For SignInFindings you rarely need it: to count only what's new, keep the highest Id you've read and request SignInFindings/$count?$filter=Id gt 4711 (see Findings of the sign-in rules).

Check and change log

Every change is checked before it's applied. A change is a transaction: compare the version, write, record in the change log, and then check the entire content the way a reload checks it—including the rules a person names. If it doesn't pass, everything is rolled back, and the 422 response names every error. Without this check, a misspelled rule would sit in the store, every reload would reject it, and the service would silently stay on the old version, even though the API had reported success. An applied change takes effect immediately (the service reloads without waiting for PollSeconds). The service also answers a constraint of the store itself (unknown Item, duplicate key, sub-entry without a parent row) with 422 and SQLite's message.

Change log. In the same transaction as the change: time, caller (Person (Kennung) (person (ID)), service name, Windows account or Einrichtung (setup)), operation (insert, update, delete, command), set or command, key, values before and after as JSON. A connection string and connection properties appear only as “neu gesetzt” (newly set), a key never. A change that was rolled back leaves no entry; an applied one always has one. Sub-entries deleted together with their parent row aren't listed individually.

Right and first administrator

Right. A rule must name the resource admin literally; * isn't enough, as with the sql right. The rule's Databases don't restrict the administration—it belongs to no data source. A rule for the administration:

{ "Id": "verwaltung", "Resources": [ "admin" ], "Verbs": [ "*" ] }

With "ReadOnly": true, the same rule reads the administration and tests connections but changes nothing (for example for an audit):

{ "Id": "revision", "Resources": [ "admin" ], "Verbs": [ "*" ], "ReadOnly": true }

You can sign in to the administration with a personal key and a Windows account, with a service key only if Gateway:Store:RequireAdminTotp = false and no sign-in rule requires a second factor for the administration—a new store contains such a rule (otherwise 403 “… gehört zu keiner Person” (… doesn't belong to any person), because a service key can't have a second factor), and not with a SQL Server login: SQL Server checks its password at a data source, and the administration belongs to none. A personal key that is restricted to data sources never reaches the administration; a read-only key can at most read it. Every address of the API responds only over a protected connection: HTTPS, a trusted proxy with X-Forwarded-Proto: https, or a request from the same computer. Otherwise 403, also for the one-time link.

First administrator. Only someone named by a rule with admin reaches the administration. The first such person is created by the init-admin command on the server, not by a request over the network, see First administrator and recovery.

One-time link. issue-key with "delivery": "link" returns an address instead of the key, valid for Gateway:Store:PickupMinutes (default 15 minutes, see configuration). When opened, the page behind it shows only a button; only that button's POST shows the key, once. This way, an email client or chat app that opens links for a preview doesn't pick up the key beforehand. The waiting key is held only in the process's memory, never on disk; a restart invalidates open links. The pickup is recorded in the change log (Einmal-Link (one-time link), with origin).

<a id=“one-time-link-by-email”></a>One-time link by email. issue-key and enroll-totp with "delivery": "mail" return an address for an invitation mail (person, delivery, link, linkExpiresAt), valid for mail_link_hours or MailLinkHours (default 72 hours). Unlike an ordinary one-time link:

  • No secret is waiting. The store holds the request and the SHA-256 checksum of the link, not a key. The key or second factor is created only on pickup; until then, an existing factor of the person stays unchanged. A copy of the store file therefore opens nothing.
  • Survives a restart of the service.
  • Exactly once. Opening it a second time shows “bereits abgeholt am … um …” (already picked up on … at …) (410) with a request to report this to an administrator if the recipient didn't do it themselves; an expired or unknown link gives 404.
  • Response without a key ID: the key exists only after pickup; it then appears in PersonalKeys. The service rejects an expiresAt before the end of the link with 400.
  • Log: issuing as a command with delivery: mail, pickup as issue-key or enroll-totp with the actor Einmal-Link von <Aussteller> (one-time link from <issuer>) and the origin.
  • If the request can no longer be fulfilled on pickup (person removed), the page shows this with 409; the link then remains unused.
{ "person": "Jane Doe", "label": "Excel report", "expiresAt": "2027-03-31T00:00:00Z",
  "readOnly": true, "databases": [ "fachdaten" ], "from": [ "10.0.0.0/8" ], "exceptFrom": [],
  "delivery": "key" }

Responses

For all sets and commands of the administration:

ResponseMeaning
401 “Es wurde keine gültige Anmeldung vorgelegt.” (No valid sign-in was presented.)no key or an unknown key
403 “Die Verwaltung ist nur über HTTPS oder vom eigenen Rechner erreichbar.” (The administration can only be reached via HTTPS or from the same computer.)unprotected connection
403 “Die Verwaltung ist für diesen Aufrufer nicht freigegeben.” (The administration isn't enabled for this caller.)no rule with admin; read-only rule on a change; key restricted to data sources
400unknown field, missing required field, set without this operation (create PersonalKeys, change ChangeLog), lifting a revocation, expiry too late
412the row was changed since it was read
422content invalid after the change, or a constraint of the store; the message names the reason

Connection test and service account

test-connection. Body { "dataSource": "fachdaten" }. The response names dataSource, reachable, elapsedMilliseconds, productVersion, database, and login; if the data source can't be reached, also message (the server's message in its own language), number, and failure. The test opens the connection once, without the connection pool, and reports the server's answer; the database driver doesn't retry it. Only after 233 (“no process is on the other end of the pipe”) does it open a second time at once: a SQL Server on the same computer sometimes reports a refused login over Shared Memory or Named Pipes that way instead of with 18456. If the server answers 233 again, it really can't be reached:

{ "dataSource": "fachdaten", "reachable": false, "elapsedMilliseconds": 7,
  "message": "Login failed for user 'NT SERVICE\\Wkl.Data.Gateway'.", "number": 18456, "failure": "Login" }

number is the database's number (for SQL Server, the error number); empty if there is none. failure classifies the failure independently of the server's language; a program uses it to explain the cause and suggest the remedy:

failureMeaningSQL Server, examplesRemedy
LoginSign-in refused: unknown login, wrong password, locked, expired18456, 18452, 18470, 18486–18488Create or enable the login in SQL Server (database administration)
DatabaseLogin accepted, the database is missing or not accessible to it4060, 4064, 911Create the user in the database or check the database name
PermissionConnected, a permission is missing229, 230, 262, 297, 300, 916Grant the permission in the database
NetworkServer not reached: name, port, service stopped, firewall, encryption refused2, 26, 53, 258, 10060, 10061, severity 20 or higherCheck name, port, and firewall
TimeoutNo answer in time-2Server load, Connect Timeout
UnreadableThe server answers but can't read the database605, 823, 824, 942, 945Check the drive, restart the database server
ConfigurationSettings unusable: string rejected, provider not usable–Correct the data source's connection or provider
OtherAny other failure–Read message and number

A provider built for an older version of the provider contract doesn't return number and failure; they then stay empty.

service-account. No body. The account under which a data source with Windows authentication (Integrated Security) signs in to the database—the name a database administrator creates the login for:

{ "account": "NT SERVICE\\Wkl.Data.Gateway", "kind": "Virtual",
  "networkAccount": "FIRMA\\SRV-DATEN$", "domain": "FIRMA" }
FieldContent
accountThe service's account; this is how a SQL Server on the same machine sees it
kindVirtual (virtual service account NT SERVICE\…), NetworkService, LocalSystem, LocalService, Managed (group managed service account DOMAIN\name$), or User
networkAccountHow a SQL Server on another machine sees it: for Virtual, NetworkService, and LocalSystem the computer account DOMAIN\MACHINE$, for a domain account that account itself. Empty if no other machine knows the account (machine without a domain, LocalService, local user account)—Windows authentication then works only against a SQL Server on the same machine
domainThe machine's domain (NetBIOS name); empty without a domain

What SQL Server needs for this account is described under Installation—notes.

On-demand keys, sign-in rules, explanation

On-demand keys. issue-key with "onDemand": true issues a key that is dormant until an administrator activates it. activate-key with { "id": "k…", "hours": 4 } activates it; a new activation replaces the running one. The service rejects (400, the message names the reason) a key that isn't an on-demand key or is revoked or expired, a duration above max_activation_hours, and an activation beyond the key's expiry date. deactivate-key with { "id": "k…" } ends it immediately. Both are recorded with the actor in the change log; the response names activeUntil.

Sign-in rule in one step. A rule applies as soon as it's saved. Via the sets, the rule is created first and its attributes afterward—in between, a deny applies to everyone, including the person saving it. save-sign-in-rule writes the rule and its attributes in one change:

{ "scope": "All", "method": "Any", "effect": "Deny", "label": "außen nur Frank",
  "exceptFrom": [ "10.0.0.0/8" ], "exceptPersons": [ "John Doe" ] }

Further lists: from, persons, windowsGroups, exceptWindowsGroups, countries, exceptCountries, operators, exceptOperators, clients, exceptClients, hours, exceptHours (meaning under sign-in policy). The service stores network operators as AS with the number and time windows in full form (Mon-Fri becomes Mon,Tue,Wed,Thu,Fri 00:00-24:00). mode is Enforce (default) or Report for a rule on trial. Exceptions with an end go in exceptions, each with persons, windowsGroups, countries, from, operators, clients, hours, until (UTC) and label:

{ "scope": "Data", "effect": "Deny", "label": "Germany only",
  "exceptCountries": [ "DE" ], "exceptFrom": [ "10.0.0.0/8" ],
  "exceptions": [ { "persons": [ "John Doe" ], "countries": [ "TH" ],
                    "until": "2026-11-02T00:00:00Z", "label": "vacation" } ] }

With "id", the command replaces the rule including all its attributes and exceptions; without it, it creates a new one. Response { "id" }. The service answers an unknown person or rule with 400, content that is invalid afterward with 422; in both cases nothing is saved.

Explain sign-in. explain-sign-in uses the same policy as a real sign-in to calculate what applies to a person—for each of the person's sign-in methods (Windows account, SQL Server login, each personal key) and each origin given:

{ "person": "John Doe", "scope": "Data",
  "origins": [ { "name": "Firmennetz", "address": "10.1.2.3" },
               { "name": "Außen", "address": "203.0.113.7" },
               { "name": "Thailand", "address": "203.0.113.7", "country": "TH" },
               { "name": "Unbekannt", "address": null } ] }

An origin may state its country itself with country; that way you can ask “what applies to John in Thailand?” without knowing an address there. Without country, the service reads the country from the geolocation database as it does for a real sign-in. Likewise, an origin takes operator (network operator, AS3320; without it, from the geolocation database), client (kind of program; without it, unknown) and at (point in time in UTC; without it, now), for example { "name": "Saturday with Excel", "address": "203.0.113.7", "client": "Power Query", "at": "2026-10-24T10:00:00Z" }.

For each sign-in method (ways), the response contains one cell per origin with result—allowed, code (second factor required), denied or a state that applies before the policy: revoked, expired, rests (on-demand key is dormant), personDisabled, outsideKeyNetworks—and the matching rules (id, version, label, effect, method, scope); floor shows the lower bound of the administration, country the country the verdict used (empty when it's unknown or no rule names countries), operator, client and at the other signals judged, tried the rules on trial that would apply (like rules; they don't go into result), and spared the rules an exception lifts (id, version, rule, effect, exception, until). id and version are the same values a sign-in event names (see Grounds of the decision), so you can check whether a rule still stands as it did at an earlier sign-in. A Windows account is assessed without its groups, because only a real sign-in knows them; if a rule names groups, groupsUnknown is included. 1 to 16 origins per call.

Sign-in log and geolocation database

The sign-in log records every sign-in decision with its origin in the log file next to the store (settings under `Gateway:SignInLog`, procedure in Check sign-in patterns and origins). The service writes the collected decisions once a minute and applies the sign-in rules as it does, so a sign-in shows up in the sets up to a minute later. All sets are read-only. SignInFindings reads stored findings; the other sets are computed when read.

SetKeyContent
SignInHoursHour, Method, Outcome, Country, NetClasscount (Count) per hour (UTC, on the full hour), plus Weekday (1 = Monday) and HourOfDay by the server's clock. No person, no address; kept for HourDays days
SignInRuleReportsIdper rule on trial and version, over the retention of the events: Rule (identifier), RuleVersion, Label, Effect, Would (sign-ins it would have hit), Spared (sign-ins an exception would have lifted it for), Persons and Origins (number of distinct ones), FirstAt, LastAt. Counts only, without person or address, so readable without the right sign-in-events. Counted are sign-in events as the log keeps them: one successful sign-in per caller, method, and origin per SuccessWindowMinutes
SignInOriginsIdper named network as a whole (NetName, Network empty), otherwise per subnet (Network, /24 or /48), with Country, CountryName, City, Latitude, Longitude, Asn, AsnOrganisation, the counts Successes, Refusals, Challenges, Persons, and FirstAt/LastAt
SignInEventsIdthe individual events, at most 20,000 of the matching ones per request, newest first. The service already reads conditions on Person, At, and Id from the log file, so the limit applies only to what matches. Fields: At, Repeats, Method, Scope (data, admin), Alias, Outcome, Reason, Status, Person, KeyId, Presented, Origin, Network, NetClass, NetName, location and network operator as above plus Region, Client, CorrelationId, Rules, Signals (see Grounds of the decision), Weekday, HourOfDay. Only with the sign-in-events right, otherwise 403
SignInFindingsIdthe stored findings of the sign-in rules, Id ascending and stable: Rule, RuleVersion, Level (act, ask), At, Person, KeyId, Observed, Usual, BaselineDays, Text, Advice, EventIds (the Id values of the underlying events, comma-separated). Person, KeyId, Usual, and EventIds only with the sign-in-events right, otherwise empty. At most 20,000 per request
SignInAdviceIdnotable patterns of the last seven days, computed when read, Id valid only within one request: Code, Text, Advice, FromAt, UntilAt, Count, Origin, Country; Person and KeyId only with the sign-in-events right. The text never names a person
GeoIpFilesKindper file (City, Asn): DatabaseType, BuildDate, AgeDays, Stale (older than StaleDays), Bytes, InstalledAt, Attribution. Empty without files
GeoIpSourceIdone row: Source, Account, HasLicenseKey, UpdateDays, LastAttemptAt, LastError, Attribution. The license key is never returned

Values. Method: apiKey, personalKey, windows, sqlLogin, none. Outcome: success, refused, challenge (a request without credentials or an intermediate step of a Windows sign-in; also the prompt for the second factor), totpSuccess, totpFailure. NetClass: loopback, private (a named network, RFC 1918, unique local IPv6 addresses, link-local, 100.64.0.0/10), public, unknown. In SignInHours, Country is empty when no country is known. Reason (for refused): notSignedIn, unknownKey, revoked, expired, rests, personDisabled, outsideKeyNetworks, denied, deniedByCountry (every refusing rule names countries), noPerson, enrolmentOpen, noFactor, secondFactorMissing, sessionExpired, limit, unprotected (a sign-in accepted only over a protected connection arrived unencrypted). Presented holds a submitted, unknown SQL Server login name—never a key. Client is the kind of program from the User-Agent header (Power Query, WinHTTP, PowerShell, Browser, or the first word); empty without the header.

Grounds of the decision

Every sign-in event the sign-in policy judged names, in Rules, the rules the decision was based on, and in Signals the signals it was judged by—both as JSON text. An event without a valid first factor (wrong key, wrong password) never reaches the policy; both fields are empty there.

Rules is a list; empty ([]) when no rule applied. Each entry names:

FieldMeaning
idThe rule's identifier: its Id in SignInRules, or file:n for the n-th rule of the configuration file—which occurs only without an administration store, so never in events the administration API reads. floor stands for the administration's lower bound (RequireAdminTotp), which is no rule
versionA fingerprint of what the rule does: 16 hexadecimal digits that change as soon as the part, method, effect, a condition, or an exception with its end changes—not when only the note changes. Equal rules have the same version everywhere
effectdeny or requireTotp
labelThe rule's note at the time of the sign-in
sparedtrue when an exception lifted the rule for this sign-in; absent otherwise
modereport for a rule on trial: it decided nothing, the entry shows what it would have done; absent for an enforced rule
exceptionThe note of the exception that lifted the rule

Signals names what went into the decision beyond the event's columns:

FieldMeaningReliability
countryThe country the rules judged by; absent when it was unknownobserved, derived from the address (VPNs and mobile networks shift it)
countryAskedWhether a rule names countries, so that the country was looked up–
secondFactorvalid (valid session shown), missing (demanded, not shown or expired), notRequired, exchange (the request opens a session or sets up the factor), notChecked (refused before)verified
totpExemptWhether the personal key is exempt from the second factorverified
operatorNetwork operator the rules judged by (AS3320); missing when it was unknown or no rule names network operatorsobserved, derived from the address
clientKind of program the request stated; missing without a User-Agentclaimed
trustFor each signal named, its level: observed, claimed or verified (for secondFactor)–

Method, person, key, and origin are in the event's columns (Method, Person, KeyId, Origin, NetClass, NetName); Client is a claim of the program and decides only when a rule asks for the program. The log folds equal denials of a minute only when the same rules in the same version decided them. To search, use contains, for example $filter=contains(Rules,'"id":"7"') for every sign-in rule 7 took part in.

The sign-in-events right. A rule must name the resource literally, in addition to admin; neither * nor admin alone grants it. The address and person behind each sign-in are personal data; the right separates who sees counts and patterns from who can follow individual people. It applies to SignInEvents, to the person fields of SignInFindings and SignInAdvice, and to `sign-in-profile`. Without the right, these fields are empty before the request's filter applies, so a condition on them matches nothing.

Findings of the sign-in rules

Each time it writes, the service applies the sign-in rules to the new events and stores whatever a rule detects as a finding. A finding is written once and never re-evaluated; it names the rule with its thresholds (RuleVersion) and the comparison period (BaselineDays) it was raised under. The same situation—rule, origin or person, hour or day—yields only one finding.

Since the last look. Id rises with every finding and is never reused. A program remembers the highest Id it has read and from then on asks only for what is new with $filter=Id gt 4711. With a condition on Id, the service reads the oldest matching findings first, so a program reads even more than 20,000 new findings completely, in steps. Without such a condition, it returns the newest.

Levels. act calls for action: an attack pattern or a key that was passed on. ask is worth a question to the person: something is new or unusual for them. Where the service reports findings is set by `Gateway:SignInLog:Alerts`.

RuleLevelDetected when
failureWaveactat least 20 refused sign-ins from one address within an hour
sprayactrefusals for at least 5 different names or keys from one address within an hour
distributedactrefusals for the same name from at least 10 addresses within an hour
impossibleTravelacttwo successful sign-ins of one person at least 500 km apart, faster than 1,000 km/h
keyManyOriginsactone personal key used from at least 5 public networks in a day
newCountryaska country the person didn't sign in from in the previous 90 days
newClientaska kind of program the person didn't use in the previous 90 days
newNetworkOperatoraska network operator the person didn't sign in through in the previous 90 days
newMethodaska sign-in method the person didn't use in the previous 90 days
unusualHouraskan hour in which, and next to which, the person never signed in during the previous 90 days

The rules about something new speak only once the person had at least 5 successful sign-ins in the previous 90 days, unusualHour only from 20; before that, everything would be new. Days and hours count in the time zone of Gateway:SignInLog:TimeZone. Observed is the value seen (a country, a count, a distance); Usual holds the person's usual values for comparison. A finding's text never names a person.

Sign-in profile

sign-in-profile shows what is usual for a person when signing in—for example, to put a newCountry finding in context. Body:

{ "person": "Frank Mustermann", "days": 365 }

days counts back from today, today included, in the time zone of Gateway:SignInLog:TimeZone; default 90, allowed 1 to 3,650. The response holds person, days, from, until, total (successful sign-ins), and activeDays, and under dimensions, per characteristic—country, network, operator, client, method, hour (hour 00 to 23)—the values with value, count, firstDay, and lastDay, most frequent first. An excerpt:

{ "person": "Frank Mustermann", "days": 365, "from": "2025-10-21", "until": "2026-10-20",
  "total": 1184, "activeDays": 213,
  "dimensions": { "country": [ { "value": "DE", "count": 1170, "firstDay": "2025-10-21", "lastDay": "2026-10-20" },
                               { "value": "IT", "count": 14, "firstDay": "2026-07-28", "lastDay": "2026-08-08" } ] } }

Only successful sign-ins count. The profile comes from daily values per person, which stay in the log file longer than the individual events. It requires the sign-in-events right (otherwise 403) and always covers exactly one person; there is no list across persons. Unknown person: 404; days out of range: 400.

set-geoip-source. Body:

{ "source": "maxmind", "account": "123456", "licenseKey": "…", "updateDays": 7 }

source is required: none (files only through upload-geoip), dbip-lite, or maxmind. updateDays 0 to 90: after that many days, the service downloads again on its own (0 = only on command); after a failed attempt, it tries again after six hours. licenseKey is stored sealed with the master key; if the body omits it, the stored key stays, and "removeLicenseKey": true removes it. maxmind without account ID or license key: 422. The change log only records whether a key is set.

update-geoip. No body. Downloads both files and puts them into use without a restart. The response is files, each with kind, databaseType, buildDate, bytes, changed, attribution; a file with the same build date as the current one stays as it is (changed: false). Refused with 422 and the reason: no source chosen, the source refuses or delivers nothing, the file is unusable, or a download is already running.

upload-geoip. The body is the file itself (.mmdb, .mmdb.gz, or MaxMind's .tar.gz), Content-Type: application/octet-stream. Query options: source=dbip-lite|maxmind sets the attribution; force=true also accepts a file that is older than or as old as the current one. The service recognizes the kind (City, Asn) from the content. The response is like one entry of update-geoip. Larger than GeoIp:MaxMegabytes: 429 (LIMIT_EXCEEDED), like any oversized body; unusable or older than the current file: 422.

Geolocation database sources

SourceRetrievalLicense and attribution
dbip-liteDB-IP Lite, monthly, no account; the service downloads dbip-city-lite-YYYY-MM.mmdb.gz and dbip-asn-lite-YYYY-MM.mmdb.gz from download.db-ip.com (early in a month, the previous month if needed)CC BY 4.0; attribution required: “IP Geolocation by DB-IP (https://db-ip.com)”
maxmindMaxMind GeoLite2 City and ASN, with the account ID and license key of a free MaxMind accountMaxMind's GeoLite2 license; notice “This product includes GeoLite2 data created by MaxMind”

The only outbound traffic is the file download; a caller's address never leaves the server.

Load log

The load log records what each shape of request costs (settings under `Gateway:LoadLog`, procedure in Investigate what loads the service and the database). A shape is a request without its values: path, alias, kind, object, compared fields, sort order, and window, with every value written as ?. Requests that differ only in their values are one shape. The service writes the collected figures once a minute into the log file, so a request appears in the sets up to a minute later. All sets are read-only. LoadFindings reads stored hints; the other sets are computed when read.

SetKeyContent
LoadShapesIdPer shape over the last seven days: Path, Alias, Kind, Resource, Text (the shape), Template (the generated statement without values), FirstAt, LastAt, Requests, TotalMs, AverageMs, P95Ms, MaxMs, DataMs, Rows, AverageRows, Bytes, Errors, Refused, Persons, Advice (codes from LoadAdvice, comma-separated)
LoadHoursHour, ShapeIdPer hour (UTC, truncated to the hour) and shape: Requests, TotalMs, DataMs, MaxMs, P95Ms, Rows, Bytes, Errors, Refused, plus Weekday (1 = Monday) and HourOfDay by the server's clock. No person; kept for HourDays days
LoadPersonsDay, ShapeId, PersonPer day (UTC), shape, and caller: Requests, TotalMs, Rows. Only with the load-persons right, otherwise 403; kept for PersonDays days
SlowRequestsIdSingle requests from SlowMilliseconds on, the newest 20,000: At, ShapeId, Ms, DataMs, Rows, Bytes, Status, CorrelationId; Person and KeyId only with the load-persons right, otherwise empty. Kept for SlowDays days
LoadFindingsIdThe hints from LoadAdvice, stored once an hour, Id ascending and stable: At (when the hint was raised), Code, ShapeId, Alias, Resource, Text, Advice, Requests, and AverageDataMs (figures of the seven days it is based on). One hint per code and shape; a shape that stays expensive doesn't appear again. No person. At most 20,000 per request; Id gt as for the findings of the sign-in rules
LoadAdviceIdShapes of the last seven days with a known remedy, computed when read, Id valid only within one request: Code, ShapeId, Alias, Resource, Text, Advice, Requests, AverageDataMs. The text never names a person

Values. Path: odata (OData surface), cf (own routes under /api/v1/cf/), checked (checked statement), passthrough (statement with the sql right), admin (administration), other (second factor, self-service, downloads). Kind: read, count, aggregate, call, insert, update, delete, statement, describe (statement only described), service (request that reached no data: metadata, a refusal, the administration). The service keeps a statement's text without literals and comments; long value lists are folded into ?, …. Skipped rows ($skip) appear in Text only as a class, never with their number: skip=small (below 1,000), skip=medium (below 10,000), skip=large (10,000 and more).

Times and amounts. TotalMs is the whole time of the request in the service; DataMs is the part from opening the connection to the data source through the last row read, including the switch to the person's identity. The 95th percentile (P95Ms) is the upper bound of the time class it falls into (classes 5, 10, 25, 50, 100, 250, 500, 1,000, 2,500, 5,000, 10,000, 30,000 ms; above that, MaxMs). Bytes counts the response before compression. Errors counts 5xx responses, Refused counts 4xx responses. Person is the person's name; without a person, it is the caller's identifier as in the audit. Sums of time, rows, and bytes are floating-point numbers so that a program such as Access, which reads integers as 32-bit, doesn't overflow.

Advice (LoadAdvice). Each code is a pattern that loads the database regardless of the data:

CodeDetected whenUsual remedy
wholeSetReading without a filter returns at least 1,000 rows per request on averageSet a filter, select only the columns you need
unindexedFilterFilter on a table with at least 10,000 rows (the catalog's estimate), and none of the compared columns leads an indexHave an index created with the first filter column, or filter on an indexed column
deepPaging$skip of 10,000 rows or moreNarrow with a filter, or read on through the next link
countLargeTotal count ($count) with at least 500 ms in the data layer on averageRequest the total only when needed
aggregateWholeAggregates without a filter with at least 500 ms in the data layer on averageNarrow the set, or have the database precompute it
expensiveStatementOwn statement with at least 1,000 ms in the data layer on average, or a 95th percentile of 2,500 ms or moreCheck it in Query Store; store it as a procedure or indexed view
refreshLoopThe same shape at least 600 times in one hour, from at most three callersCheck the automatic refresh

The load-persons right. A rule must name the resource literally, in addition to admin; neither * nor admin alone grants it. The load per person shows who worked how much and when; the right separates who sees the service's load from who can compare people.

export-load. Optional body:

{ "days": 30 }

days ranges from 1 to HourDays; the default is 30. The response is a JSON file wkl-load-YYYYMMDD-hhmmss.json in the format wkl-load-export/1 with shapes, hours, advice, and the bounds of the time classes (histogramBounds). It contains the names of aliases, objects, and columns, but no persons, keys, addresses, correlation IDs, or values; the daily figures per person and the single slow requests aren't even read for it. The required right is read access to the administration; the export is recorded in the change log.

Finding a shape in SQL Server

The service marks every statement it generates with the identifier of its shape as a comment after the first keyword, for example SELECT /* wkl:5b9b83f10953 */ TOP (1) … or UPDATE /* wkl:8b7d7b280081 */ [dbo].[Probe] SET …. In that position, the comment is also kept by Query Store, which doesn't store a comment in front of or behind a statement. For a procedure call, the identifier precedes the call; Query Store then records the procedure's statements without it. With this identifier, a database administrator finds the shape in the plan cache, in Query Store, or in Extended Events and gets from there to LoadShapes. Checked statements carry the identifier as well; a statement with the sql right runs unchanged, without an identifier. The connections identify themselves to SQL Server as Wkl.Data.Gateway (program_name) unless the data source's connection string sets an Application Name of its own.

Database updates

The service's update module applies signed database updates (packages <product>-schema-<version>.zip) to the databases for which the folder `Gateway:Updates` holds a protected configuration. It checks every PollSeconds seconds for packages in the inbox whose copy finished at least 5 seconds ago, and for announced packages whose deadline has passed. The commands show the state and carry out the administrators' decisions.

Rights. update-databases, update-status, and update-history read (a rule with admin is enough, even a read-only one). Every decision needs the right to change and is written to the change log with the database, the package, and the outcome.

update-databases. No body. The databases with a trusted configuration and their settings, plus the files the module leaves out:

{
  "folder": "C:\\ProgramData\\Wkl.Data.Gateway\\updates",
  "databases": [ {
    "id": "demo-prod", "gatewayAlias": "demo", "product": "Demo", "dialect": "sqlserver",
    "connection": { "string": "Server=sql01;Database=Demo;User ID=wkl_update;Encrypt=True",
                    "secret": "file:C:\\ProgramData\\Wkl.Data.Gateway\\secrets\\demo-update.txt" },
    "keys": [ "9c0f6d1e…" ],
    "inbox": "D:\\Updates\\Demo", "archive": "D:\\Updates\\Demo-Archiv",
    "backup": { "mode": "native", "target": "D:\\Sicherung" }, "starterSource": null,
    "settings": { "expand": "automatic", "contract": "confirm", "noticeMinutes": 60, "sessionWindowMinutes": 15 }
  } ],
  "problems": [ { "file": "alt.json", "code": 9, "message": "…" } ]
}
FieldContent
idKey of the database in every other command (database)
connection.stringConnection for structure changes and backups, without a password
connection.secretReference to the password (env:NAME or file:PATH); the value is never returned
keysPublisher keys as SHA-256 fingerprints
settingsOperating settings: expand and contract (automatic or confirm), lead time of an announcement (noticeMinutes), time window in which a request through the service counts as an open session (sessionWindowMinutes). They are changed in the set `UpdateSettings` and apply from the next pass. If they cannot be read, the database waits instead of running on the defaults
problemsFiles left out, with code and reason; 9 means not trusted (users other than Administrators and SYSTEM can change it, a password in the connection, connection.secret missing, file name ≠ id)

update-status. Body { "database": "demo-prod" }. Response: the status object of database updates (format 2), the same as wkl-db status --json:

{
  "format": 2, "id": "demo-prod", "product": "Demo", "schema": 13,
  "packages": [ { "id": 41, "file": "Demo-schema-14.zip", "schema": 14, "kind": "contract",
                  "status": "announced", "reason": null, "message": null, "deadlineUtc": "2026-11-02T18:00:00Z" } ],
  "sessions": [ { "product": "Demo", "version": "2.3.1", "path": "gateway", "host": "10.1.2.17", "user": "FIRMA\\mustermann", "lastSeenUtc": "2026-11-02T16:40:12Z" } ],
  "drift": []
}

packages holds the packages that are neither applied (done) nor dismissed (dismissed): waiting (waits for confirmation), announced (the announcement is running; deadlineUtc is the deadline), running, failed, refused. reason is the code that explains the status (see below). sessions lists sessions of older applications that hold an announced package back, and sessions that name no product and version, which don't hold it back; the service identifies sessions that come through it by the header `X-Wkl-Application`. drift lists objects changed outside a package since the last one, each with object (name with schema), type (kind of object, such as table or view), and change (changed, missing; dateChanged is only a hint for an encrypted module and holds no package back).

update-history. Body { "database": "demo-prod", "skip": 0, "take": 20 } (take 1 to 100, default 20). Finished packages (done, failed, refused, dismissed), newest first, with id, file, schema, kind, status, reason, message, receivedUtc, confirmedBy, confirmedUtc, sha256, and steps: per step phase (predeploy, pre, model, post, postdeploy, baseline), version, path, sha256, appliedUtc, appliedBy, durationMs, result (ok, failed, rolledback), and message.

Decisions. Body { "database": "demo-prod", "package": 41 }; package is the number (id) from the status or the history.

CommandFor statusEffect
confirm-updatewaitingexpand: runs at once. contract: is checked and announced (a notice to the applications, deadline after the package's lead time or noticeMinutes) and runs at the deadline as soon as no older application is open. The database records gateway:<name> (<id>) as the person who confirmed
dismiss-updatefailed, refusedBecomes dismissed and leaves the status; the file stays in the folder failed
retry-updatefailed, refusedTakes the unchanged file from failed again, with every check, as a new package; the old one becomes dismissed. A changed package needs a new file
move-update-deadlineannouncedBody also "deadlineUtc": "2026-11-03T06:00:00Z"; not in the past. Applications read the new deadline when they next poll
cancel-update-noticeannouncedCancels the announcement; the package waits again (reason 7) and needs a new confirmation, also with contract = automatic

upload-update. POST /admin/v1/upload-update?database=demo-prod&file=Demo-schema-14.zip, body = the ZIP file, at most 64 MiB. The service puts it into the inbox and takes it at once, like a file dropped there. The file name is a plain name without a path.

Response to a decision or an upload. message says what happened; package says what became of the package when it ran or was announced (otherwise null):

{ "message": "Angekündigt bis 2026-11-02 18:00:00Z.",
  "package": { "file": "Demo-schema-14.zip", "exitCode": 0, "outcome": "announced", "message": "Angekündigt bis 2026-11-02 18:00:00Z." } }

outcome is done (applied; the file is in the archive and in done), announced, waiting (still waits, for example for confirmation or for sessions), or failed (refused or failed; the file is in failed). exitCode and reason use the same codes:

CodeMeaning
0OK
2Package invalid or not trusted (signature, checksum, publisher, product, kind)
3Database doesn't fit: higher schema version, or objects of its own without a history
4A script or the deployment plan failed; everything was rolled back
5Backup failed
6Sessions of older applications are still open
7Waits for confirmation; the message names the reason
10Another run is applying a package of this product right now
11Drift: an object was changed outside a package
12Migrations don't match the history (checksum, missing or out-of-order migration)
13The plan could cause data loss
ResponseMeaning
404No database with this id, or no package with this number
409The package has another status than the command needs; on retry, the file in failed is missing or has changed; on upload, the same package is already waiting
400Required field missing, file name with a path, deadline in the past
413File larger than 64 MiB
503The module is applying a package right now (try again shortly), or the database can't be reached

Many operations at once

There's no bulk route: neither OData $batch nor a command for multiple people or keys. Each person, each assignment, each key is a separate request.

QuestionBehavior
How many requests in a rowThe administration counts against the caller's rate limit like any other request: Gateway:Security:RateLimit:PerCaller (default 1200) per RateLimit:WindowSeconds (default 60), after a pause up to RateLimit:Burst (default 600) at once, see configuration. Above that, the service responds with 429 and says in Retry-After after how many seconds the next request passes. There's no separate limit just for the administration
Partial failuresEach request is its own transaction. If the 300th request fails, the 299 before it stay saved; nothing is rolled back. The caller continues with the failed one
Repeating after a connection dropWith the Idempotency-Key header here too: it takes effect for every method except GET and HEAD, so also when creating via OData and for the commands. Rules and retention (10 minutes, in memory only, 401/429/5xx not stored) are described in Repeated write requests
Repeating after a restart or after more than 10 minutesWithout a stored response. Check beforehand whether the record already exists: the service rejects a second person with the same name, but a second key would really be created

Worked example: 847 people, each with create, rule assignment and key, come to roughly 2,500 requests, so with the default the first 600 go at once and the rest at 20 a second, so a little over a minute and a half in total. If you need to be faster, raise PerCaller for the duration of the setup.

The effort can often be avoided. Only those who get personal keys, a second factor or their own default identity need to be people. Rights and sign-in rules can be assigned to a Windows group as a whole (WindowsAccounts, IdentityAssignments, attribute windowsGroups of the sign-in rules), without entering its members individually. A second factor, however, always belongs to a person: if a rule requires one for a group, every member without a person entry is rejected with 403 (see Sign-in).

Members of a Windows group

The service doesn't deliver member lists. It never queries Active Directory: it takes a caller's groups from their Windows sign-in, and it resolves names to SIDs only when saving. If you want to create all members of a group as people, read them out yourself, at a workstation or in a script (for example Get-ADGroupMember or net group /domain), and then create a person with WindowsAccount for each member. The service doesn't pick up later joins and departures in the group on its own; rights granted via the group itself, however, follow the membership without any maintenance.

See also

en/wiki/data-conectivity/database-gateway/reference/administration-api.txt · Last modified: by Wolfgang van der Stille