API Reference
The REST API manages everything the web UI does: organizations, partners, certificates, protocol profiles, plugin instances, routes and messages. This page explains how the API behaves and walks through common tasks. The field-by-field reference is the OpenAPI document the server serves at /api/v1/openapi.yaml.
- Basics
- Errors and validation
- Lists, filters and pagination
- Resources
- Plugin configurations
- Certificates
- Messages
- Events
- Notifications
- Audit log
- Walk-through: onboarding a partner
Basics
- Base path:
/api/v1, on the API server’sHTTP_PORT(default 8080). - Authentication: every
/api/v1request, the OpenAPI document included, is authenticated by the server’sAuthenticatorand answered401(with the error envelope) when refused; the request carriesAuthorization: Bearer <token>, either a MicroB2B API token (mb2b_..., OpenAPI schemeapiToken) of a local service account, namedsa:<name>in the audit log, or (schemebearerAuth) a JWT issued by an enabled OpenID Connect identity provider. The developer build accepts a request without anAuthorizationheader as the userdeveloper; a credential it is given is checked exactly as in a release build. A release build chooses the provider by the token’s issuer (issmust be a provider’s issuer URL exactly) and validates the token locally (signature against that provider’s keys, issuer, audience = that provider’s API audience, the client id when empty, expiry, not-before); a token of an issuer no enabled provider has is401INVALID_TOKEN; a machine client gets one with the client credentials grant, see deployment. The caller is the MicroB2B user of the token’s identity (issuer and subject), which the audit log names (a disabled user’s token is401INVALID_TOKEN), or the federated service account registered for it; a valid token of any other subject is401INVALID_TOKEN. A request without the header is401MISSING_AUTH_HEADER; an API token that is unknown or revoked, or whose account is disabled, is401INVALID_TOKEN, an expired one401TOKEN_EXPIRED. A browser session cookie is no credential here. The examples below send$ACCESS_TOKEN, an API token or an access token of an identity provider; against a developer build, leave theAuthorizationheader out (a token it is given is checked). OnlyGET /healthis public. A request body above 1 MiB (20 MiB for a certificate upload) is answered413with codePAYLOAD_TOO_LARGE. - Format: JSON in and out, except certificate upload (
multipart/form-data) and the downloads. A request with a body must declare it:Content-Type: application/json(optionally; charset=utf-8),multipart/form-datafor the upload. EveryPOST,PUT,PATCHandDELETEdeclaresContent-Type: application/jsonalso when it has no body (aDELETE, a notifier’s test, a service action). Any other type, or none, is answered415with codeUNSUPPORTED_MEDIA_TYPEand anAcceptheader naming the expected type, before anything happens. The certificate upload also carries anX-Requested-Withheader with any value (XMLHttpRequestby convention); without it the answer is400with codeMISSING_HEADER. A web page of another origin can send neither without the CORS preflight, which CORS refuses unlessALLOWED_ORIGINSlets the origin in, so a page open in a browser cannot change anything through the API. AGETneeds no Content-Type. - Envelope: every JSON response is
{"data": ..., "error": null}on success and{"data": null, "error": {...}}on failure.GET /healthis the exception: it answers{"status": "healthy", "timestamp": ..., "database": "up"}unwrapped, or503with"unhealthy". - OpenAPI document: assembled at startup from the core API and the part each installed module (AS2, file, HTTP poster, notifications) contributes, so it describes exactly the endpoints and plugin types of the running server:
curl http://localhost:8080/api/v1/openapi.yaml -o openapi.yamlErrors and validation
An error is {"code": ..., "message": ..., "details": {...}} under error; details is present when there is something to add.
| Status | When |
|---|---|
| 400 | INVALID_INPUT: a malformed body, path ID or query parameter, or a failed validation; MISSING_HEADER: a certificate upload without X-Requested-With |
| 401 | The request is not authenticated; the code says why: MISSING_AUTH_HEADER, INVALID_AUTH_HEADER (not Bearer <token>), INVALID_TOKEN (the message never says what is wrong with the token) or TOKEN_EXPIRED |
| 404 | NOT_FOUND: no such record (Organization not found) |
| 409 | CONFLICT: a record with the same name already exists (an organization, partner, route, identity provider (also by issuer URL), plugin configuration, or the profile of that party); CERTIFICATE_EXISTS: the same certificate is stored; CERTIFICATE_IN_USE: a certificate that profiles still use is not deleted (see Certificates) |
| 415 | UNSUPPORTED_MEDIA_TYPE: the request declares another Content-Type than the operation takes, or none (see Basics) |
| 500 | INTERNAL_ERROR: something failed on the server; the cause is logged there, with the request’s user, and never told to the client |
Every record resource (organizations, partners, routes, certificates, AS2 profiles, plugin configurations) answers its errors through the same mapping, so the same failure is the same status and code everywhere.
A body that fails validation is answered with 400, code INVALID_INPUT, message Validation failed, and one entry in details per rejected field, keyed by its JSON property name. A failure that blames no single field is under _form; an unusable query parameter (limit, offset, a filter) is reported the same way, under the parameter’s name. A body that is not a JSON object is INVALID_INPUT with message Invalid request body and what the decoder said under details.body.
{
"data": null,
"error": {
"code": "INVALID_INPUT",
"message": "Validation failed",
"details": {
"partner_certificate_id": "Messages to this partner are encrypted to the partner's certificate, but none is chosen. Choose the partner's certificate, or set the encryption algorithm to None."
}
}
}The rules are the web form’s, not a separate set: organizations, partners, routes, certificate descriptions, plugin configurations and protocol profiles are validated by the same generated code on both paths, so a submission is accepted or refused identically, with the same messages. That covers required fields, lengths and ranges, the allowed values of algorithm and mode fields, that referenced certificates exist (and have a private key where they sign or decrypt for us), and the cross-field rules.
Creates start from the defaults the web form’s create page shows, so a property the body leaves out has its default, and answer 201 with the stored record.
Updates. PUT applies the body to the stored record, so a property the body leaves out keeps its value, validates the result and answers 200 with the stored record. The path names the record: a body that names another (id; local_organization_id or business_partner_id for an AS2 profile; name for a plugin configuration) is refused with a validation failure on that property.
Deletes send Content-Type: application/json without a body and answer 204 without a body. A certificate that a profile still refers to is not deleted (409 CERTIFICATE_IN_USE, see Certificates).
Timestamps (created_at, updated_at, …) are RFC 3339 strings.
Lists, filters and pagination
List responses hold the page under a plural key (organizations, partners, certificates, routes, profiles, messages, notifications) with total (records matching the filters), limit and offset. An item of a list has the same shape as the record’s own GET.
limitdefaults to 20 (50 for messages and notifications) and is at most 500 (a larger value is 500),offsetto 0. A value that is not a whole number (at least 1 forlimit, 0 foroffset) is a400naming the parameter, on every list.- Filters combine; an unknown filter value is a
400.
| Endpoint | Filters |
|---|---|
GET /organizations, GET /partners | q: text in the name or display name, case-insensitive |
GET /certificates | q: text in the description, common name, subject, issuer or serial number, or the start of the SHA-256 fingerprint (at least four hex digits); status: valid, expiring (within 30 days) or expired; key: present or absent |
GET /certificates/expiring | days: warning window, default 30 |
GET /messages | status: Received, InProgress, Retrying, AwaitingReceipt, Stalled, Delivered, NotRouted, Refused, Error; from, to: party name; since: RFC 3339 time, created at or after (anything else is a 400) |
GET /events | message_id |
GET /notifications | severity, code, instance, outcome, from, to, include_tests (see Notifications) |
curl -H "Authorization: Bearer $ACCESS_TOKEN" "http://localhost:8080/api/v1/messages?status=Error&limit=10"
curl -H "Authorization: Bearer $ACCESS_TOKEN" "http://localhost:8080/api/v1/certificates?q=acme&key=absent"Resources
All paths are below /api/v1. {id} is a numeric ID, except for messages (a UUID).
| Path | Methods | What |
|---|---|---|
/organizations, /organizations/{id} | GET, POST, PUT, DELETE | Our parties: name (1 to 64 lower case letters, digits, _, -) and display_name |
/partners, /partners/{id} | GET, POST, PUT, DELETE | Business partners, same fields |
/certificates, /certificates/{id} | GET, POST, PUT, DELETE | Certificates |
/certificates/import | POST | Import the chosen certificates of an upload |
/certificates/expiring | GET | Expired and expiring certificates, soonest first |
/certificates/{id}/download | GET | The certificate as PEM (never the key) |
/as2/local-profiles, /as2/local-profiles/{id} | GET, POST, PUT, DELETE | AS2 profile of an organization; {id} is the organization’s ID |
/as2/remote-profiles, /as2/remote-profiles/{id} | GET, POST, PUT, DELETE | AS2 profile of a partner; {id} is the partner’s ID |
/routes, /routes/{id} | GET, POST, PUT, DELETE | source_plugin, from, to, target_plugin; from and to may be * |
/oidc-providers, /oidc-providers/{id} | GET, POST, PUT, DELETE | Identity providers users sign in at and whose access tokens this API takes |
/service-accounts, /service-accounts/{id} | GET, POST, PUT, DELETE | Service accounts, the non-human identities of this API |
/api-tokens, /api-tokens/{id} | GET, POST, DELETE | API tokens of the local service accounts; DELETE revokes |
/plugins/configs?type=..., /plugins/configs/{type}/{name} | POST; GET, PUT, DELETE | Plugin configurations |
/messages, /messages/{id} | GET | Messages |
/messages/{id}/payload?stage= | GET | One payload stage of a message, receipt included |
/events | GET | Live stream of message history |
/stats | GET | Dashboard counters and plugin status |
/notifications | GET | The notification log |
/openapi.yaml | GET | The OpenAPI document |
What the AS2 profile fields mean, and their defaults, is in the AS2 configuration guide; what routes do in Routing. A REST create of a partner AS2 profile starts from the same secure defaults as the form. The partner profile’s receipt_timeout_hours (1 to 720, default 24) is the time the sender allows for the partner’s asynchronous MDN.
Stats. GET /stats returns messages_last_24h (sent_last_24h outbound plus received_last_24h inbound), messages_last_7d, in_flight_messages (Received, InProgress, Retrying or AwaitingReceipt), failed_last_24h (messages that reached Error or Stalled; Refused messages are not counted), system_status (Degraded when more than 10 messages failed in the last 24 hours, otherwise Healthy) and plugin_status, keyed <type>:<name>: Enabled for a configured instance, and Running, Stopped or Error for one running in the process that answers.
Plugin configurations
Plugin instances are configured by type and name. The types are those of the installed modules: as2-receiver, as2-sender, file-poller, file-writer, http-poster, notification-webhook. Their fields are described in the AS2 guide, in Routing and, for the notifier, in Notifications. POST /api/v1/plugins/configs/notification-webhook/{name}/test sends a test notification and answers with the notification log’s row of it.
POST /plugins/configs?type=<type>creates an instance. A property the body leaves out gets the default the web form’s create page shows (enabledistrue, intervals, patterns, ports and retry settings have theirs). Missingtypeis400 MISSING_TYPE, an unknown one400 INVALID_TYPE. An instance name is lower case (letters, digits,_and-) and unique across all types, compared without regard to case: anamethat an instance of another type already holds (enabled or not) is a validation failure onnamethat names the type, and the web form refuses it the same way.GET,PUTandDELETE /plugins/configs/<type>/<name>address one instance. Anamein aPUTbody other than the path’s is a validation failure onname.- Write-only values are never returned, by any response (get, create and update echoes, lists, error details): the passwords (
http_basic_password,basic_password) and the additional header lists (http_headers,headers), because a header such asX-Api-Keyis a credential. An update that leaves one out keeps the stored value (a header list is replaced by sending a list, an empty one removes them). The stored values belong to the URL they were entered for: an update that changesurl(AS2:endpoint) has to send them again, or it is refused with a validation failure on the property. The web form shows a stored header asName: ********and keeps its value as long as that line stays. - A URL that carries a credential, as userinfo or in a query parameter named
token,api_key,passwordand the like, is refused. - There is no list endpoint;
GET /statsnames every configured instance. - Instances are read when the plugins start, so a change takes effect after the instance restarts (see Routing). Routes are not: a saved route applies to the next message dispatched.
file-writerandhttp-postertakemax_concurrent_deliveries(1 to 100; default 1 and 5): how many deliveries the instance works on at the same time. The AS2 sender’s is the same property.
curl -X POST "http://localhost:8080/api/v1/plugins/configs?type=file-poller" \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "outbox", "base_directory": "/data/outbox", "processed_directory": "/data/processed"}'answers 201 with the stored configuration, defaults filled in ("path_pattern": "{to}/{filename}", "file_pattern": "*.*", "polling_interval": 5).
Certificates
Upload with multipart/form-data: certificate (required: a certificate, bundle, PKCS#12 file or key, in any supported format), optionally private_key, password (for PKCS#12) and description.
- A single certificate (with or without its key) and a
descriptionis imported at once:201with the certificate. - Anything else imports nothing and answers
200with what was found (found.certificates,found.keys,notes) and animport_tokenthat expires after ten minutes.POST /certificates/importwith{"import_token": "...", "certificates": [{"index": 0, "description": "..."}]}then imports the chosen ones, each with the key paired with it (201).
A certificate is identified by its SHA-256 fingerprint and stored once, with or without a key. Uploading one that is already stored (in any format) is 409 CERTIFICATE_EXISTS, naming the stored certificate (details.existing_id); an import of several stores all or none. The web UI refuses it with the same message. To replace a certificate with its key-bearing twin, delete the stored one first.
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "X-Requested-With: XMLHttpRequest" \
-F "description=MyCompany signing" -F "[email protected]" -F "[email protected]" \
http://localhost:8080/api/v1/certificatesA certificate is always shown the same way, by the list, GET /certificates/{id}, the update, the upload and the import: the details the certificate page shows (names, validity, state and days_remaining, key and signature algorithms, extensions, fingerprints, has_private_key and private_key_matches), never the private key. PUT /certificates/{id} with {"description": "..."} changes the description, the only editable property.
DELETE /certificates/{id} refuses a certificate that a record still refers to: an AS2 profile of an organization or a partner, in any of its certificate fields. The answer is 409 CERTIFICATE_IN_USE; the message names the profiles and the fields, and details.users lists them (kind, name, fields, and link, the web UI page where the profile is changed; at most 25, details.total counts them all). Change those profiles first, then delete. The check and the delete are one transaction. The certificate’s page in the web UI lists the same records under Used by, and its Delete button is refused with the same message.
{
"data": null,
"error": {
"code": "CERTIFICATE_IN_USE",
"message": "certificate 3 is still used by AS2 profile of partner \"Globex\" (Partner Certificate); change these first",
"details": {
"users": [{"kind": "AS2 profile of partner", "name": "Globex", "fields": ["Partner Certificate"], "link": "/ui/partners/2/as2-profile/edit"}],
"total": 1
}
}
}GET /certificates/expiring?days= takes a whole number of at least 1 (anything else is a 400 naming days).
Messages
GET /messages/{id} returns the message with:
stage: where the message is in its handling:accepted(its source still owns it),dispatched(it has deliveries that are not final) orsettled;direction:inbound(received from a partner) oroutbound(created here to be sent);deliveries: one per target plugin instance, by creation and target, each withid,target,state(queued,delivering,retrying,awaiting_ack,delivered,failed,stalled),attempts,next_attempt_at(set for aretryingdelivery, otherwisenull),last_error,created_at,updated_atandfinished_at. The message’sstatusis derived from them;history: what happened, oldest first, each line withcreated_at,kind(status_changed,delivery_changed,mdn_received,mdn_refused,duplicate_received,redispatch_requested,redeliver_requested), andold_status/new_statusortext; the lines about a delivery also carrydelivery_id,targetand, fordelivery_changed,old_state/new_state;payloads: the payload stages the message has, withsize_bytesandstored_at. each is downloaded withGET /messages/{id}/payload?stage=...(the web UI’s is/ui/messages/{id}/payload?stage=...);protocols: what each protocol module knows, by module name.protocols.as2describes the receipt and MICs (see Reading the receipt); a module with nothing to say has no entry.
Redispatch and redeliver
Two actions put a message’s work back in motion. Neither takes a body, but both are POSTs and declare Content-Type: application/json like every state change. Both answer 200 with the message as GET /messages/{id} shows it afterwards, 404 NOT_FOUND for a message or delivery that does not exist (a delivery of another message is not found either), and 409 when the message or delivery is not in a state the action applies to. The user who asked is named in the history line (redispatch_requested, redeliver_requested) and in the operator action log line.
POST /messages/{id}/redispatchroutes the message again: the routes are matched now, a target the message has no delivery for gets one, and queued and stalled deliveries are queued again. Delivered, failed, retrying, delivering and awaiting-receipt deliveries are left alone. Allowed for every message that has left the stageaccepted; a message in that stage is refused with409 NOT_REDISPATCHABLEwithdetails.stageanddetails.allowed(the stages that qualify). A call that changes nothing (no new target, nothing queued or stalled) is not an error: it answers200with the unchanged message and writes no history line.POST /messages/{id}/deliveries/{delivery_id}/redeliversends one delivery again: it becomesqueuedwith no attempts counted and no last error. Allowed for a delivery that isfailed,stalled,retryingordelivered(sending a delivered delivery again, for an operator who lost the file or the downstream copy: the target receives the payload a second time, and an AS2 partner sees a repeat with the same Message-ID, may answer with the stored receipt or none, and without a receipt the delivery fails at the receipt deadline). A delivery that isqueued,deliveringorawaiting_ackis refused with409 NOT_REDELIVERABLEwithdetails.stateanddetails.allowed.
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
http://localhost:8080/api/v1/messages/$MESSAGE_ID/redispatchGET /messages/{id}/payload?stage=<stage> downloads one stage of the message’s content (application/octet-stream): unpacked, message, received, headers or receipt (for AS2 the MDN as it travelled, offered as <id>-receipt.eml). Without stage it is unpacked, or the first stage the message has. An unknown stage is 400 INVALID_INPUT on stage; a stage the message does not have is 404, and no other stage is served instead.
Events
GET /events is a Server-Sent Events stream (text/event-stream) of the lines added to message histories. Each event is named message_history; its id is the line’s ID and its data a JSON object with id, message_id, created_at, kind and, as applicable, old_status, new_status and text.
- Only lines after the connection are sent, unless the client sends
Last-Event-ID(anEventSourcedoes so on reconnect): then every stored line after that ID is sent first. - If more was missed than the server replays, or the client reads too slowly, it receives one
reloadevent and the stream closes: list the messages again and reconnect withoutLast-Event-ID. - A comment is sent every 20 seconds. A reverse proxy must not buffer or time out the stream.
curl -N -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:8080/api/v1/eventsid: 1
event: message_history
data: {"id":1,"message_id":"57d9fda1-...","created_at":"2026-10-05T00:44:14Z","kind":"status_changed","new_status":"Received","text":"Picked up from file by outbox, queued for sending"}Services: plugin instances
GET /services lists every plugin instance the plugin manager runs: type, name, status (running, stopped, error, starting, stopping), error (why an instance is in error), controllable, state_since (when it entered the status) and actions (what can be done to it now: start, stop, restart).
POST /services/{type}/{name}/{action} (action is start, stop or restart) performs the action, waits for it to end and answers with the instance in its new state.
startis valid for a stopped instance or one inerror,stopfor a running one,restartfor a running one or one inerror. Anything else, and any action on an instance that is starting or stopping, is a409.- The API server is not controllable: every action on it is a
403. - An unknown instance is a
404(an instance is its type and name together); an unknown action is a400. - A start that fails leaves the instance in
error, raises theplugin_failedalert and answers500with the reason. A stop raises no alert.
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
http://localhost:8080/api/v1/services/file-poller/inbox/restartSettings
The global settings of the installation, the payload storage (storage), how long a delivery is looked after and how MicroB2B names itself to other systems, are one record:
GET /settingsanswers{"data": {...}}withstorage_backend(localors3),storage_local_directory,storage_s3_bucket,storage_s3_region,storage_s3_endpoint,delivery_max_dispatches(1 to 20, default 5: how often a delivery that no target claims is announced again before it is marked stalled),identification_hide_version(defaultfalse: theUser-Agentof the requests MicroB2B sends and theServerheader of its AS2 answers areMicroB2B/<version>;truemakes themMicroB2B), and besides themupdated_at,storage_in_use(the storage the API server’s process runs with) andrestart_needed(the saved storage differs from it: every process reads the settings when it starts).PUT /settingstakes the same properties; one the body leaves out keeps its saved value. The rules are the settings page’s: local storage needs a directory, S3 storage a bucket and a region, the endpoint is an absolute http(s) URL without credentials,delivery_max_dispatchesis a whole number from 1 to 20,identification_hide_versiona boolean. A failure is a400INVALID_INPUTnaming the properties.POST /settings/storage/teststores, reads back and deletes a probe blob with the saved settings, within 10 seconds, and answers200withok,storage,elapsed_msand, when it failed,stepandproblem. S3 credentials are never part of the settings or of an answer.
curl -X PUT -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"storage_backend": "local", "storage_local_directory": "/var/lib/microb2b/payloads"}' \
http://localhost:8080/api/v1/settingsIdentity providers
The OpenID Connect providers (what they are and how to register MicroB2B at one); the same records as Access > Identity providers, with the same rules. GET /oidc-providers answers {"data": {"oidc_providers": [...], "total", "limit", "offset"}}. A provider has id, name (lower case letters, digits, _, -; unique; its redirect URI is <PUBLIC_URL>/ui/auth/<name>/callback), display_name (the login button’s “Sign in with enabled, issuer_url (unique; exactly as in the provider’s discovery document; https, http only for localhost), client_id, client_secret, scopes (requested at sign-in, separated by spaces, must include openid when sign_in is on; default openid profile email), api_scopes (scopes every API access token must carry in scope or scp, separated by spaces; "" for none), api_audience ("": the client id), created_at, updated_at, sign_in (default true: a “Sign in with” button; false for a provider that only issues access tokens to service accounts, which needs no client) and accept_tokens_without_audience (default false; accepts a token whose aud is empty from a registered federated service account only). client_secret is write-only: required on create when sign_in is on, kept by an update that leaves it out, required again by an update that changes issuer_url (400 INVALID_INPUT on client_secret otherwise), and never in a response. A name or issuer URL another provider has is 409 CONFLICT. A change counts from the next sign-in and the next token; nothing is restarted. Creating, changing and deleting a provider are audit events of kind oidc-provider.
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"keycloak","display_name":"Company login","enabled":true,"issuer_url":"https://keycloak.example.com/realms/b2b","client_id":"microb2b","client_secret":"..."}' \
http://localhost:8080/api/v1/oidc-providersService accounts
The non-human identities of this API (what they are); the same records as Access > Service accounts, with the same rules. GET /service-accounts answers {"data": {"service_accounts": [...], "total", "limit", "offset"}}, by name. An account has id, name (lower case letters, digits, _ and -; unique; its requests are sa:<name>), description, kind (local or federated), enabled, oidc_issuer and oidc_subject (federated: the issuer URL of an identity provider and the sub of the account’s tokens, unique together; "" for a local account), last_used_at (null for never), created_at and updated_at. Name and kind cannot change (400). A taken name or identity is 409 CONFLICT. DELETE /service-accounts/{id} revokes the account’s tokens and removes it. Creating, changing and deleting an account are audit events of kind service-account.
GET /api-tokens lists the tokens of every local account, {"data": {"api_tokens": [...], ...}}, the newest first. A token has id, public_id (the 12 characters after mb2b_), service_account (its account’s name), label, state (active, expired, revoked), created_at, expires_at, last_used_at and revoked_at (null for never). POST /api-tokens takes service_account (a local account), label (1 to 64 characters, no control characters) and expires_in_days (1 to 365, default 90) and answers 201 with the token under token, the only answer that ever carries it. DELETE /api-tokens/{id} revokes the token, 204; a token is never changed. Making and revoking a token are audit events of kind api-token.
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"ci-deploy","description":"Deploys from CI"}' \
http://localhost:8080/api/v1/service-accounts
curl -X POST -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"service_account":"ci-deploy","label":"runner","expires_in_days":30}' \
http://localhost:8080/api/v1/api-tokensNotifications
GET /notifications lists the notification log (what the webhook notifiers did with each alert, see Notifications), newest alert first, one row per alert and notifier. The answer is {"data": {"notifications": [...], "total", "limit", "offset"}}; a row has id, alert_id, alert_time, severity, code, subject_kind, subject_id, subject_name, summary, details, dedupe_key, link, instance, format, test, outcome, attempts, last_error, created_at and delivered_at.
- Filters:
severity(info,warning,critical),code(an alert code),instance(a notifier name),outcome(queued,delivered,failed,suppressed_cooldown,grouped),fromandto(RFC 3339 or a date,2006-01-02, in UTC:fromis inclusive;tois exclusive, except that a date includes its whole day) andinclude_tests(the test button’s notifications are left out unlesstrue). limitdefaults to 50 and is at most 500 (a larger value is 500);offsetdefaults to 0.- Every parameter is checked: a bad value, and a parameter that is not known, is a
400INVALID_INPUTwith the parameter named indetails.
curl -H "Authorization: Bearer $ACCESS_TOKEN" "http://localhost:8080/api/v1/notifications?severity=critical&outcome=failed&from=2030-01-01"Audit log
The changes to configuration, newest first (how it is recorded and verified). The log is read-only: no endpoint creates, changes or removes an event.
GET /auditanswers{"data": {"events": [...], "total", "limit", "offset"}}. Filters:kind,action,actor,record_id,record_name,fromandto(RFC 3339, or a date; a date astonames that whole day), withlimit(default 50) andoffset. A parameter that is not acceptable, or not known, is a400naming it. An event hasv,id,at,actor(the subject of the user, orsystem),action,kind,record_id,record_name(the name at that moment, so a deleted record stays readable),changes(field,old,new;secret: truefor a secret that changed, without values),prev_hash,hash(empty without an audit key) andrecord_gone.POST /audit/verifyrecomputes the hash chain and answers200withok,events_checked,chained,unchained,head_id,head_hashand, for a log that does not verify,first_broken_idandreason. It answers409AUDIT_KEY_REQUIREDfor a log with hashed events when the process has no key.
Walk-through: onboarding a partner
Sending files to a new AS2 partner acme. Assumes organization 1 with an AS2 profile, an AS2 sender sender_main and a file poller outbox already exist (see the AS2 example).
API=http://localhost:8080/api/v1
AUTH="Authorization: Bearer $ACCESS_TOKEN"
# 1. The partner
curl -X POST -H "$AUTH" -H "Content-Type: application/json" $API/partners \
-d '{"name": "acme", "display_name": "ACME Corporation"}'
# {"data":{"id":1,"name":"acme","display_name":"ACME Corporation",...},"error":null}
# 2. Its public certificate
curl -X POST -H "$AUTH" -H "X-Requested-With: XMLHttpRequest" -F "description=ACME" -F "[email protected]" $API/certificates
# {"data":{"id":2,"description":"ACME","common_name":"ACME",...},"error":null}
# 3. Its AS2 profile: signed, encrypted, synchronous signed MDN by default
curl -X POST -H "$AUTH" -H "Content-Type: application/json" $API/as2/remote-profiles \
-d '{"business_partner_id": 1, "as2id": "ACME", "endpoint": "https://as2.acme.example/as2", "partner_certificate_id": 2}'
# 4. Route files the outbox poller picks up for acme to the sender
curl -X POST -H "$AUTH" -H "Content-Type: application/json" $API/routes \
-d '{"source_plugin": "outbox", "from": "*", "to": "acme", "target_plugin": "sender_main"}'A file placed in /data/outbox/acme/ is sent to ACME. Follow it with GET /messages?to=acme or the event stream.