API Integration Reference

The Merlonix API is a REST API over HTTPS. It enables agencies to automate asset provisioning, trigger checks, create attestations, and pull monitoring data into external tools — project management systems, reporting dashboards, client portals.


Base URL

https://api.merlonix.com/v1

All endpoints require authentication.


Authentication

The API uses Bearer token authentication. Include your JWT in every request:

Authorization: Bearer <your-token>

Obtaining a token: Navigate to Settings → API in the Merlonix app to generate an API token. Tokens are scoped to your tenant and inherit the permissions of the user who created them.

Token expiry: API tokens are long-lived by default. Rotate them from Settings → API if you suspect a token has been compromised.


Request and response format

  • All request bodies are JSON (Content-Type: application/json)
  • All responses are JSON
  • Timestamps are ISO 8601 UTC: 2026-04-30T09:14:00Z

Error format

{
  "error": "bad_request",
  "message": "hostname is required",
  "issues": [
    { "path": "hostname", "issue": "Required" }
  ]
}

Common status codes:

CodeMeaning
200Success
201Created
400Validation error (see issues)
401Missing or invalid token
403Authenticated but not authorised
404Resource not found or not in your tenant
429Rate limit exceeded

Pagination

List endpoints return paginated results.

Request parameters:

ParameterTypeDefaultDescription
limitinteger50Max records per page (1–100)
offsetinteger0Records to skip

Response shape:

{
  "data": [...],
  "total": 142,
  "limit": 50,
  "offset": 0
}

Assets

Assets are the core monitoring unit: a hostname or IP address that Merlonix monitors for SSL and DNS health.

List assets

GET /v1/assets

Query parameters: limit, offset

Response:

{
  "data": [
    {
      "id": "uuid",
      "label": "Client A — main site",
      "hostname": "clienta.com",
      "check_interval_minutes": 60,
      "ssl_monitoring": true,
      "dns_monitoring": true,
      "created_at": "2026-01-15T10:00:00Z"
    }
  ],
  "total": 12,
  "limit": 50,
  "offset": 0
}

Create an asset

POST /v1/assets

Request body:

{
  "label": "Client A — main site",
  "hostname": "clienta.com",
  "check_interval_minutes": 60,
  "ssl_monitoring": true,
  "dns_monitoring": true
}
FieldTypeRequiredNotes
labelstringYesHuman-readable name
hostnamestringYesDomain or IP to monitor
check_interval_minutesintegerNo5, 15, 30, or 60 (default: 60)
ssl_monitoringbooleanNoDefault: true
dns_monitoringbooleanNoDefault: true

Response: 201 Created with the created asset object.

Asset limits: Asset creation fails if your plan's asset limit is reached. Check GET /v1/customers/me for asset_limit and current usage.


Get an asset

GET /v1/assets/:id

Returns a single asset with the same shape as the list response, plus the latest check summary.


Update an asset

PATCH /v1/assets/:id

All fields are optional. Omitted fields are unchanged.

{
  "label": "Client A — shop (updated)",
  "check_interval_minutes": 5
}

Delete an asset

DELETE /v1/assets/:id

Deletes the asset and all associated check history. This is irreversible.

Response: 200 OK with { "deleted": true }.


Trigger an immediate check

POST /v1/assets/:id/check-now

Enqueues an out-of-cycle check for the asset. The check runs within 60 seconds.

Response: 200 OK with a trace ID you can use to correlate the check result in GET /v1/checks.


Attestations

Attestations are tamper-evident certificates tied to an asset. See Attestation Deep Dive for a full explanation of verification methods and certificate contents.

List attestations

GET /v1/attestations

Query parameters: limit, offset, asset_id (optional filter)


Create an attestation

POST /v1/attestations
{
  "asset_id": "uuid",
  "statement": "Logo pack v3 — approved by client on 2026-04-29",
  "method": "inline"
}
FieldTypeRequiredNotes
asset_iduuidYesAsset to attest
statementstringYesDescription of what is being certified
methodstringYesinline, dns_txt, or http_file

For dns_txt and http_file methods, the response includes a challenge object:

{
  "id": "uuid",
  "status": "pending",
  "challenge": {
    "method": "dns_txt",
    "hostname": "_merlonix-verify.clienta.com",
    "record_type": "TXT",
    "token": "merlonix-verify=abc123xyz"
  }
}

Place the challenge in DNS or serve the file, then call POST /v1/attestations/:id/verify to complete verification (or wait — Merlonix polls automatically every 30 seconds for up to 10 minutes).


Get an attestation

GET /v1/attestations/:id

Returns the full certificate including verification status, state hash, and the public verification URL.


Revoke an attestation

POST /v1/attestations/:id/revoke
{
  "reason": "Superseded by v4 delivery"
}

The reason is optional but displayed on the public verification URL and logged in the audit trail.


Checks

Checks are the time-series record of SSL and DNS monitoring results for each asset.

List check history

GET /v1/checks?asset_id=<uuid>

Query parameters:

ParameterTypeNotes
asset_iduuidRequired
check_typestringssl or dns (optional filter)
limitintegerDefault 50, max 100
offsetintegerDefault 0

Response:

{
  "data": [
    {
      "id": "uuid",
      "asset_id": "uuid",
      "check_type": "ssl",
      "classifier": "expiring_soon",
      "severity": "warning",
      "detail": { "days_remaining": 22, "issuer": "Let's Encrypt" },
      "checked_at": "2026-04-30T09:00:00Z"
    }
  ]
}

Get latest check per asset

GET /v1/checks/latest?asset_id=<uuid>

Returns the most recent SSL check result and the most recent DNS check result for the asset. Useful for dashboard-style status polling.


Alert channels

Channels define where Merlonix sends notifications. See Alert Channels for the full reference on severity filters and routing.

List channels

GET /v1/channels

Create a channel

POST /v1/channels
{
  "type": "slack",
  "config": {
    "webhook_url": "https://hooks.slack.com/services/..."
  },
  "severity_filter": "warning"
}
FieldTypeNotes
typestringemail, slack, or webhook
configobjectType-specific config (see below)
severity_filterstringinfo, warning, or critical

Config by type:

// email
{ "address": "[email protected]" }

// slack
{ "webhook_url": "https://hooks.slack.com/services/..." }

// webhook
{ "url": "https://your-endpoint.com/hook", "secret": "optional-signing-secret" }

For webhook type, the response includes the generated signing secret. Store it securely — it is shown only once.


Update a channel

PATCH /v1/channels/:id

Accepts the same fields as create. Omitted fields are unchanged.


Delete a channel

DELETE /v1/channels/:id

Account

Get account details

GET /v1/customers/me

Returns tenant details including plan, asset count, asset limit, and current stack tags.

{
  "id": "uuid",
  "name": "Your Agency",
  "billing_email": "[email protected]",
  "plan": "starter",
  "asset_count": 12,
  "asset_limit": 25,
  "stack_tags": ["payments.checkout", "network.cdn"],
  "trial_ends_at": null,
  "created_at": "2026-01-01T00:00:00Z"
}

Update account details

PATCH /v1/customers/me
{
  "name": "Your Agency",
  "billing_email": "[email protected]",
  "timezone": "America/New_York",
  "stack_tags": ["payments.checkout", "payments.webhooks", "network.cdn"]
}

All fields are optional. stack_tags is a full replacement — send the complete list you want active. See Vendor Stack Tags for the full tag catalog.


Automation patterns

Provisioning a client on signup

// 1. Create the asset
const asset = await api.post('/v1/assets', {
  label: `${clientName} — main site`,
  hostname: clientDomain,
  check_interval_minutes: 60,
});

// 2. Trigger the first check immediately
await api.post(`/v1/assets/${asset.id}/check-now`);

// 3. Create an alert channel for the client
await api.post('/v1/channels', {
  type: 'email',
  config: { address: clientContactEmail },
  severity_filter: 'critical',
});

Pulling check data into a report

// Get the latest check for every asset
const assets = await api.get('/v1/assets?limit=100');
const statuses = await Promise.all(
  assets.data.map(a => api.get(`/v1/checks/latest?asset_id=${a.id}`))
);
// statuses[i] has ssl and dns check results for assets.data[i]

Creating an attestation on deliverable approval

const cert = await api.post('/v1/attestations', {
  asset_id: assetId,
  statement: `${deliverableName} — approved by ${clientName} on ${date}`,
  method: 'inline',
});
// cert.verification_url is the shareable public link
await sendDeliverableEmail({ ...deliverable, certificateUrl: cert.verification_url });

Rate limits

The API applies per-tenant rate limits:

TierRequests per minute
Starter60
Agency300

When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating when the limit resets.


Next: return to Getting Started or read Vendor Stack Tags to configure which vendor incidents appear in your monitoring data.