Overview

All endpoints are JSON over HTTPS. Org scope is handled via the x-org-key header for both GET and POST requests. Don’t put org keys in query strings or URLs.

Base URL: https://machineid.io
API version: /api/v1/...
Content type: application/json

  • Org key per customer or tenant (orgApiKey).
  • Device IDs are strings you control (deviceId).
  • Limits enforced server-side by plan tier (free, pro, scale, max).
  • Stripe Checkout and portal back billing and upgrades.
Typical POST pattern
Orgs

Create an org and receive an orgApiKey. Store it with your customer record and in your agents.

Create org
POST /api/v1/org/create

Called from your backend or control plane when a new customer is created. The response includes a unique orgApiKey for that tenant.

Request (curl)

Example response:

Response
{
  "status": "ok",
  "orgApiKey": "org_123",
  "handler": "org_create"
}
Devices

Register devices against an org, validate them at the edge, list them, and revoke or restore as needed.

Register device
POST /api/v1/devices/register

Called by your agent, gateway, or service when a machine first comes online. Idempotent for the same deviceId: you can safely retry.

Request (curl)

Possible responses:

New device (under limit)
{
  "status": "ok",
  "handler": "devices/register",
  "deviceId": "agent-01",
  "planTier": "free",
  "planState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 1,
  "remaining": 2,
  "overLimit": false,
  "softGraceWindow": false
}
Already active
{
  "status": "exists",
  "handler": "devices/register",
  "deviceId": "agent-01",
  "planTier": "free",
  "planState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 1,
  "remaining": 2,
  "overLimit": false,
  "softGraceWindow": false
}
Restored from revoked
{
  "status": "restored",
  "handler": "devices/register",
  "deviceId": "agent-01",
  "planTier": "free",
  "planState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 2,
  "remaining": 1,
  "overLimit": false,
  "softGraceWindow": false
}
Plan limit reached
{
  "status": "limit_reached",
  "handler": "devices/register",
  "deviceId": "agent-99",
  "planTier": "free",
  "planState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 3,
  "remaining": 0,
  "overLimit": true,
  "softGraceWindow": false
}
Validate device
GET /api/v1/devices/validate

Called by agents before doing work (on startup or on a schedule). Returns whether a device is allowed to run.

Request (curl)

Example responses:

Active device
{
  "status": "ok",
  "handler": "devices/validate",
  "deviceId": "agent-01",
  "planTier": "free",
  "planState": "active",
  "effectivePlanState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 1,
  "overLimit": false,
  "allowed": true,
  "reason": "ok"
}
Revoked device
{
  "status": "revoked",
  "handler": "devices/validate",
  "deviceId": "agent-02",
  "planTier": "free",
  "planState": "active",
  "effectivePlanState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 2,
  "overLimit": false,
  "allowed": false,
  "reason": "device_revoked",
  "revoked_at": "2025-11-17T09:00:00.000Z"
}
List devices
GET /api/v1/devices/list

Returns all devices for an org. Used by the MachineID.io dashboard and can be wired into your own admin tools.

Request (curl)

Example response:

Response
{
  "status": "ok",
  "handler": "devices/list",
  "devices": [
    {
      "device_id": "agent-01",
      "created_at": "2025-11-17T07:23:20.926Z",
      "status": "active",
      "revoked_at": null
    },
    {
      "device_id": "agent-02",
      "created_at": "2025-11-17T08:00:00.000Z",
      "status": "revoked",
      "revoked_at": "2025-11-17T09:00:00.000Z"
    }
  ]
}
Revoke & restore
POST /api/v1/devices/revoke · POST /api/v1/devices/unrevoke

Use /devices/revoke to turn off a device, and /devices/unrevoke to restore it. Revoked devices fail validation until restored.

Revoke (curl)
Unrevoke (curl)

Example revoke response:

devices/revoke
{
  "status": "ok",
  "handler": "devices/revoke",
  "deviceId": "agent-01",
  "device": {
    "device_id": "agent-01",
    "created_at": "2025-11-16T10:00:00.000Z",
    "revoked_at": "2025-11-17T07:30:00.000Z",
    "updated_at": "2025-11-17T07:30:00.000Z"
  }
}
Usage

Check how many devices are active for an org and what cap applies to that plan.

Usage summary
GET /api/v1/usage

Use /usage in your own admin tools to show the same numbers as the MachineID.io dashboard: plan tier, active devices, and the limit for that plan.

Request (curl)

Example response:

usage
{
  "status": "ok",
  "handler": "usage",
  "planTier": "free",
  "planState": "active",
  "accessUntil": null,
  "limit": 3,
  "devicesUsed": 1,
  "remaining": 2,
  "overLimit": false,
  "isActive": true,
  "isGrace": false,
  "isFrozen": false
}
Billing

Plans are backed by Stripe. Query the current plan and open Checkout when it’s time to upgrade.

Billing summary
GET /api/v1/billing/summary

Shows the current plan tier and a short message. Free plans typically return "Free plan — upgrade available", while paid plans are managed via the Stripe customer portal.

Request (curl)

Example response:

billing_summary
{
  "status": "ok",
  "planTier": "free",
  "stripeCustomerId": null,
  "stripeSubscriptionId": null,
  "message": "Free plan — upgrade available",
  "handler": "billing_summary"
}
Stripe Checkout
POST /api/v1/billing/checkout

When upgrading from your own UI, call /billing/checkout with the target plan in the JSON body ("pro", "scale", or "max") and send the org key in the x-org-key header. The response includes a Stripe checkoutUrl you can redirect to.

Request (curl)

In environments where Stripe is not configured, you’ll see status: "no_stripe" and a message explaining that Checkout is unavailable.

Status & errors

Responses are intentionally small and consistent. Log status and handler for quick debugging.

  • "ok" – success.
  • "exists" – device already active.
  • "restored" – device was revoked, now active again.
  • "limit_reached" – plan cap hit for that org.
  • "error" – input or validation issue.
  • "not_found" – org or device not found.

Examples:

Missing org key
{
  "status": "error",
  "error": "missing_params",
  "handler": "usage"
}
Org not found
{
  "status": "not_found",
  "handler": "usage"
}
Limit reached (register)
{
  "status": "limit_reached",
  "deviceId": "dev-999",
  "planTier": "free",
  "limit": 3,
  "devicesUsed": 3,
  "handler": "devices/register"
}
Device input error
{
  "status": "error",
  "error": "missing_params",
  "handler": "devices/register"
}
Automation & agents

Designed to be safe for scripts, services, and AI tools working alongside human operators.

Whether you call the API from a CLI, a background job, or an AI agent acting on your behalf, the shapes are kept small and predictable so automation stays safe and boring:

  • Stable JSON envelopes with status and handler on every response.
  • devices/register is idempotent for a given deviceId – safe to retry.
  • limit_reached is explicit, so callers can surface an upgrade prompt instead of hammering.
  • No hidden state beyond org key, plan tier, and device records.

Recommended behavior:

  • On limit_reached, stop creating devices and surface an upgrade path or alert.
  • On error like missing_params, prompt for configuration instead of retrying blindly.
  • On transient network issues or rate limiting, retry with backoff from your automation layer.

Think of MachineID.io as a simple, central source of truth your agents and tools can trust – built by people who ship agents, for people who ship agents.