Skip to content

New feature: Our API is live: Webhook, MCP, or API - you decide. Try it out!Join beta

API

Ask Reploya from your own tools which apps it manages, which version is live in which ring, what waits for approval, and how recent releases went. Read only, with a key per tool or an Entra application.

How the API works

The API answers GET requests with JSON. It can read, never approve, release or change anything, so a leaked key exposes status data but can never push software to devices. Everything it returns, every member of your organization already sees in the dashboard.

Base URL
https://app.reploya.com/api/v1

Two ways in, both sent as Authorization: Bearer: an API key, or an Entra access token of an application in your organization's own Microsoft tenant. Either reads every endpoint, for its own organization only.

API keys

  1. In the dashboard, open External access, then API access, and choose Create API key.
  2. Name: say where the key is used, so you know which one to revoke (1 to 80 characters, unique in your organization).
  3. Expiry: 30 days, 90 days, 1 year or No expiry; 1 year by default. An expired key stays listed as Expired until you revoke it.
  4. The dialog shows the key once, with a copy button and a ready curl example. It starts with rpk_. Store it now; it is not shown again. Reploya keeps only a one way fingerprint of it.
  • Who manages keys: owners and admins (the tenant.manage permission) create and revoke them. Every member sees the list: name, the first characters of the key, created, last used and expiry.
  • Revoke deletes a key at once; tools using it get 401 on their next request. Keys cannot be edited or extended: create a new key, switch the tool over, then revoke the old one.
  • Keys belong to the organization, not to a person: a key keeps working when the member who created it leaves. At most 10 keys per organization, expired keys included.

Authentication

Send the key in the Authorization header of every request:

Header
Authorization: Bearer rpk_…
Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/organization"
  • Never put a key in a URL, a query string or a browser page. The API sends no CORS headers, so web pages on other sites cannot call it; call it from a server, a script or a tool like n8n.
  • The API answers on the app host only, https://app.reploya.com/api/v1. Another Reploya host answers 421 instead of redirecting, because a client following a redirect may drop or leak the Authorization header.
  • Cookies are ignored. A request is its key or token, nothing else.
  • An Entra access token goes in the same header; see OAuth2 with Microsoft Entra.

OAuth2 with Microsoft Entra

For companies that prefer their own identity platform over a shared secret: an application or managed identity in your organization's own Microsoft tenant asks Microsoft for a short lived access token (the OAuth2 client credentials flow) and sends it instead of a key. Reploya stores no secret for it; certificates, managed identities and your Entra policies work as usual. You need an Entra admin who can grant admin consent. Keys keep working beside it.

Only tokens from the tenant your organization signs in with count, and only for applications you added in Reploya. Both are needed: the role granted in Entra, and the application added on the API access page.

Set up an app registration

  1. In the Microsoft Entra admin center, open App registrations and register an application (single tenant is fine). Add a certificate or a client secret under Certificates & secrets.
  2. Under API permissions, choose Add a permission, then APIs my organization uses, search for Reploya, pick Application permissions and tick api.read. Then Grant admin consent.
  3. In Reploya, open External access, then API access, and choose Add application. Enter a name and the Application (client) ID of the registration (not its object ID). The dialog shows your tenant ID, the token URL and the exact scope to copy.

Request a token and call the API

Token request
curl -X POST "https://login.microsoftonline.com/<your tenant ID>/oauth2/v2.0/token" \
  -d grant_type=client_credentials \
  -d client_id=<your application ID> \
  --data-urlencode client_secret="$CLIENT_SECRET" \
  --data-urlencode scope="api://<Reploya application ID>/.default"

The answer holds access_token. Send it like a key:

Request
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://app.reploya.com/api/v1/organization"

The scope is Reploya's Application ID URI followed by /.default, which asks for every role granted to your application. The API access page shows the exact value; here it reads api://<Reploya application ID>/.default.

Use a managed identity

A managed identity has no permissions page, so an admin grants the role in PowerShell (Microsoft Graph module). Use the identity's Client ID (its application ID) when you add it on the API access page.

Grant the role
Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All"
$reploya  = Get-MgServicePrincipal -Filter "appId eq '<Reploya application ID>'"
$role     = $reploya.AppRoles | Where-Object Value -eq "api.read"
$identity = Get-MgServicePrincipal -Filter "displayName eq '<managed identity name>'"
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $identity.Id `
  -PrincipalId $identity.Id -ResourceId $reploya.Id -AppRoleId $role.Id

Inside Azure, the identity asks its local endpoint for the token, with the Application ID URI as resource (no /.default). The Azure SDKs do this for you with the scope api://<Reploya application ID>/.default.

Token on an Azure VM
curl -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=api://<Reploya application ID>"

With n8n

  1. Create a credential of type OAuth2 API: grant type Client Credentials, Access Token URL https://login.microsoftonline.com/<your tenant ID>/oauth2/v2.0/token with your tenant ID, your client ID and secret, scope api://<Reploya application ID>/.default, authentication Send credentials in body.
  2. In the HTTP Request node, choose authentication Generic Credential Type, then OAuth2 API with that credential. n8n fetches and renews the token itself.

Revoke

  • Remove the application on the API access page: its tokens get 401 on the next request.
  • Removing the role or the application in Entra stops new tokens, but a token already issued stays valid until it expires: about an hour for an app registration, up to 24 hours for a managed identity. Remove it in Reploya too when it must stop at once.

Troubleshooting

  • 401 although the token request worked: decode the token (for example on jwt.ms) and look for roles. If api.read is missing, open the application's API permissions and add Reploya from APIs my organization uses (not Microsoft Graph) as an Application permission (not delegated): tick api.read, confirm with Add permissions, then choose Grant admin consent until the status shows it as granted. A new grant can take a few minutes to reach new tokens.
  • roles holds api.read and the answer is still 401: the Application (client) ID is not added on the API access page, or it was removed.

Reploya accepts only application tokens (no tokens acting for a signed in user), signed by Microsoft for Reploya, issued by your organization's tenant, and still valid.

Scopes

Every endpoint needs a scope, a named permission. Keys and Entra applications use the same names; for an application, the scope is the app role granted to it in Entra.

Scopes
ScopeGrants
api.readRead every endpoint of this page. Every key holds it, and keys created before scopes existed got it.

Write actions, such as approving a release, will come with their own scopes. A key or application gets them only when you grant them; until then it can only read. A request without the scope an endpoint needs answers 403 insufficient_scope.

Responses

  • Field names are camelCase and match the webhook messages: app.id, app.name, app.platform, version, url.
  • Times are ISO 8601 strings in UTC. url is a link to the matching dashboard page.
  • A field without a value is left out, never null. Check whether a field exists rather than comparing it with null.
  • New fields may appear at any time; ignore the ones you do not know. Removing or renaming a field would come with a new /api/v2.
  • The API never returns a person's name or email, an Entra tenant or group id, an Intune app id or a token.
Response headers
HeaderValue
X-Request-IdA new id per request. Quote it if you contact support.
X-RateLimit-Limit60, the requests a key or application may make per minute.
X-RateLimit-RemainingRequests left in the current minute.
Retry-AfterOn 429 only: whole seconds until the next minute starts.
Cache-Controlno-store: answers are never cached.

Paging

Lists answer {"items": [...], "nextCursor": "..."}. Pass nextCursor as cursor to get the next page; the last page has no nextCursor. A cursor is opaque: do not build or change one.

limit sets the page size, 1 to 100. The default is 100 for apps and 20 for approvals and releases. An app renamed between two pages can be skipped or repeated.

/organization

Your organization, and who is asking.

GET /organization

Retrieve your organization

Your organization and the credential that made the request, in caller: its type (key or application), name and scopes. For a key, expiresAt is left out when it never expires, and the same fields also appear under key. Use it to test a credential.

Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/organization"
Response 200
{
  "organization": {
    "id": "019a0b6e-3c11-7e42-8b0a-55e1c9d2f003",
    "name": "Contoso"
  },
  "caller": {
    "type": "key",
    "name": "n8n weekly report",
    "prefix": "rpk_Xk3m9QaZ",
    "expiresAt": "2027-10-20T09:00:00.000Z",
    "scopes": [
      "api.read"
    ]
  },
  "key": {
    "name": "n8n weekly report",
    "prefix": "rpk_Xk3m9QaZ",
    "expiresAt": "2027-10-20T09:00:00.000Z"
  }
}
Response 200 with an Entra token
{
  "organization": {
    "id": "019a0b6e-3c11-7e42-8b0a-55e1c9d2f003",
    "name": "Contoso"
  },
  "caller": {
    "type": "application",
    "name": "Reporting job",
    "applicationId": "6f1c2a3b-4d5e-4f60-8a7b-9c0d1e2f3a4b",
    "scopes": [
      "api.read"
    ]
  }
}

/apps

The apps your organization manages, with their versions, targets and rollouts.

GET /apps

List apps

Every app your organization manages, sorted by name, as the dashboard's Apps list shows them (removed apps and drafts included).

GET /apps query parameters
ParameterMeaning
platformOne of windows, macos.
patchStatusOne of up_to_date, update_available, updating, failed, not_assigned, not_deployed, removed, draft.
limitItems per page, 1 to 100. Default 100.
cursorThe nextCursor of the previous page. Leave it out for the first page.

Per app: latestVersion is the newest version Reploya knows, deployedVersion the one live in your tenant, pendingVersion the one a release is rolling out now. targets lists the target group, or the ring names in order. ringStage says where a ring rollout stands; its kind is waiting (with reached, total, next, dueAt), assigning (ring, total), paused (reached, total) or assigned (total).

Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/apps?platform=windows&patchStatus=updating"
Response 200
{
  "items": [
    {
      "id": "019a0b6c-4b70-7c18-8d26-e93a1f5b0c77",
      "name": "7-Zip",
      "vendor": "Igor Pavlov",
      "platform": "windows",
      "status": "active",
      "patchStatus": "updating",
      "latestVersion": "24.09",
      "deployedVersion": "24.08",
      "targets": [
        "Pilot",
        "Broad"
      ],
      "pendingVersion": "24.09",
      "ringStage": {
        "kind": "waiting",
        "reached": 1,
        "total": 2,
        "next": "Broad",
        "dueAt": "2026-10-22T08:00:00.000Z"
      },
      "url": "https://app.reploya.com/dashboard/apps/019a0b6c-4b70-7c18-8d26-e93a1f5b0c77"
    }
  ],
  "nextCursor": "WyI3LXppcCIsIjAxOWEwYjZjLTRiNzAtN2MxOC04ZDI2LWU5M2ExZjViMGM3NyJd"
}

GET /apps/{id}

Retrieve an app

One app: everything of the list item, plus currentRelease (the release rolling out now, with its rings for a ring rollout) and versions (the newest 10 versions Reploya detected, newest first). A ring has position, name, status (pending, waiting, assigning, assigned, failed, canceled) and dueAt. An unknown id answers 404.

Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/apps/019a0b6c-4b70-7c18-8d26-e93a1f5b0c77"
Response 200
{
  "id": "019a0b6c-4b70-7c18-8d26-e93a1f5b0c77",
  "name": "7-Zip",
  "vendor": "Igor Pavlov",
  "platform": "windows",
  "status": "active",
  "patchStatus": "updating",
  "latestVersion": "24.09",
  "deployedVersion": "24.08",
  "targets": [
    "Pilot",
    "Broad"
  ],
  "pendingVersion": "24.09",
  "ringStage": {
    "kind": "waiting",
    "reached": 1,
    "total": 2,
    "next": "Broad",
    "dueAt": "2026-10-22T08:00:00.000Z"
  },
  "url": "https://app.reploya.com/dashboard/apps/019a0b6c-4b70-7c18-8d26-e93a1f5b0c77",
  "currentRelease": {
    "id": "019a0b6f-1c82-7a39-9b07-6e4d2f8c0a15",
    "status": "waiting",
    "version": "24.09",
    "deliveryModel": "rings",
    "rings": [
      {
        "position": 1,
        "name": "Pilot",
        "status": "assigned"
      },
      {
        "position": 2,
        "name": "Broad",
        "status": "waiting",
        "dueAt": "2026-10-22T08:00:00.000Z"
      }
    ]
  },
  "versions": [
    {
      "version": "24.09",
      "detectedAt": "2026-10-20T06:00:00.000Z",
      "releaseNotesUrl": "https://www.7-zip.org/history.txt"
    },
    {
      "version": "24.08",
      "detectedAt": "2026-08-11T06:00:00.000Z",
      "releaseNotesUrl": "https://www.7-zip.org/history.txt"
    }
  ]
}

PATCH /apps/{id} Coming soon

Change an app's approval settings

Set how new versions of an app are approved, as on the app's approval settings in the dashboard.

/approvals

New versions that wait for a person to approve them.

GET /approvals

List pending approvals

Versions waiting for approval, newest first. tenant is the Reploya tenant connection. blocked appears when the version cannot be approved right now, with the reason code the dashboard shows (the full list is in the OpenAPI document).

GET /approvals query parameters
ParameterMeaning
appIdOnly approvals of this app (its id, a uuid).
limitItems per page, 1 to 100. Default 20.
cursorThe nextCursor of the previous page. Leave it out for the first page.
Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/approvals"
Response 200
{
  "items": [
    {
      "id": "019a0b6d-a3f0-7e55-b4c2-8d7e1a6f9b03",
      "createdAt": "2026-10-20T09:11:05.000Z",
      "app": {
        "id": "019a0b6c-6d03-7b9e-a07f-5c2e8d41b9f6",
        "name": "Google Chrome",
        "platform": "macos"
      },
      "version": "141.0.7390.66",
      "detectedAt": "2026-10-20T09:00:00.000Z",
      "tenant": {
        "id": "019a0b6b-9f21-7d84-a3c6-1e5f0b8d2a47",
        "name": "Contoso"
      },
      "url": "https://app.reploya.com/dashboard/approvals/019a0b6d-a3f0-7e55-b4c2-8d7e1a6f9b03"
    }
  ]
}

POST /approvals/{id}/approve Coming soon

Approve a version

Approve a waiting version so its release starts, as the dashboard's Approve does.

POST /approvals/{id}/decline Coming soon

Decline a version

Decline a waiting version, as the dashboard's Decline does.

/releases

Rollouts of a version to your tenant, running and finished.

GET /releases

List releases

Releases, newest first. target is the group of a single target release (a ring rollout shows its rings on the app). error says in plain English why a failed release failed.

GET /releases query parameters
ParameterMeaning
appIdOnly releases of this app (its id, a uuid).
statusOne of waiting, canceling, completed, replaced, queued, building, assigning, reconciling, active, retiring, retired, failed, canceled, superseded.
limitItems per page, 1 to 100. Default 20.
cursorThe nextCursor of the previous page. Leave it out for the first page.
Request
curl -H "Authorization: Bearer $REPLOYA_API_KEY" \
  "https://app.reploya.com/api/v1/releases?status=failed"
Response 200
{
  "items": [
    {
      "id": "019a0b70-5e14-7b62-8c90-3d1a7f4e2b86",
      "status": "failed",
      "deliveryModel": "single",
      "app": {
        "id": "019a0b6c-2e95-7a4f-b1d3-7f80c4a9e261",
        "name": "Notepad++",
        "platform": "windows"
      },
      "version": "8.7.1",
      "tenant": {
        "name": "Contoso"
      },
      "target": "All devices",
      "createdAt": "2026-10-19T14:02:11.000Z",
      "updatedAt": "2026-10-19T14:03:40.000Z",
      "error": "The target group no longer exists in Entra ID.",
      "url": "https://app.reploya.com/dashboard/apps/019a0b6c-2e95-7a4f-b1d3-7f80c4a9e261"
    }
  ]
}

POST /releases/{id}/retry Coming soon

Retry a failed release

Start a failed release again, as the dashboard's Retry does.

Errors

Every error has the same shape, with a plain English message:

Response 400
{
  "error": {
    "code": "invalid_request",
    "message": "limit must be a whole number from 1 to 100."
  }
}
Error codes
StatusCodeWhen
400invalid_requestA filter value that is not allowed, an appId that is not a uuid, a limit outside 1 to 100, or a cursor this API did not issue. The message says which.
401unauthorizedNo credential, or one that fails: a key that is malformed, unknown, revoked or expired, or a token that is not a valid Entra application token of an added application. The answer is the same in every case; WWW-Authenticate is Bearer without a credential and Bearer error="invalid_token" with one.
401organization_pending_deletionYour organization is scheduled for deletion, so every key, application and assistant connection is refused until an owner cancels it.
403subscription_inactiveYour organization's subscription is not active.
403insufficient_scopeThe key or application does not grant the scope this endpoint needs. WWW-Authenticate names it, for example Bearer error="insufficient_scope", scope="api.read".
403plan_requiredYour organization's plan does not include the API. The message names the plans that do.
404not_foundNo such app in your organization (also for an id of another organization).
405method_not_allowedAnything but GET or HEAD. The API is read only; Allow: GET, HEAD.
421wrong_hostThe request reached a host other than the app host. Use https://app.reploya.com/api/v1.
429rate_limitedMore than 60 requests in this minute. Wait the seconds in Retry-After.
500internalSomething went wrong on our side. The message holds a request id to quote to support.
503auth_unavailableEntra tokens cannot be checked right now, because Microsoft's signing keys are out of reach. Wait the seconds in Retry-After. API keys are not affected.

A failed key or token check counts against the address that sent it: too many in a minute answer 429 instead of 401.

Limits

Limits
LimitValue
Requests per key or Entra application60 per minute, counted in whole minutes
Keys per organization10
Entra applications per organization10
Items per pageup to 100

Need status changes the moment they happen? Use webhooks instead of asking every minute.

OpenAPI document

An OpenAPI 3.1 document describes every endpoint, parameter, response, enum and error. It needs no key. Import it into Postman, Insomnia or a code generator.

https://app.reploya.com/api/v1/openapi.json

Recipe: n8n

An n8n HTTP Request node that reads every app, all pages:

  1. Create a credential of type Header Auth: name Authorization, value Bearer rpk_… with your key.
  2. In the HTTP Request node: method GET, URL https://app.reploya.com/api/v1/apps, authentication Generic Credential Type, then Header Auth with that credential.
  3. Under Options, add Pagination:
    Pagination
    Mode: Update a Parameter in Each Request
      Type: Query, Name: cursor
      Value: {{ $response.body.nextCursor }}
    Pagination Complete When: Other
      Complete Expression: {{ !$response.body.nextCursor }}
  4. Split the result with {{ $json.items }} and filter, for example, on patchStatus = update_available for a weekly report.

To test the credential, point a node at https://app.reploya.com/api/v1/organization: it answers with your organization and the key's name.

Want Reploya to tell your flow when something happens instead? See the webhook docs.