Skip to content

User & account API ​

Everything about the signed-in user and the account they belong to. All endpoints need a bearer token (JWT or API key).

Most of this page needs no particular permission - it is about you. The three exceptions are marked: updating account contact details, the alert webhook, and the alert thresholds all need account:update, which an API key can never hold. Those three are session-only in practice.

Quick stats ​

http
GET /api/user/stats

A small dashboard summary - how many of each resource you're running:

json
{ "pods": 3, "domains": 2, "networks": 1, "firewalls": 1 }

Get your profile ​

http
GET /api/user/profile

Returns your user record, plus the permissions resolved for this request:

json
{
  "id": "6a4d5b83b7006ccae6391801",
  "username": "alice",
  "firstName": "Alice",
  "lastName": "Smith",
  "email": "[email protected]",
  "lastLogin": "2026-07-16T09:41:02Z",
  "isActive": true,
  "isVerified": true,
  "role": "user",
  "accountRole": "owner",
  "accountId": "6a4d5b83b7006ccae6391800",
  "acceptedTermsVersion": "2026-07-22",
  "acceptedTermsAt": "2026-07-16T09:41:02Z",
  "createdAt": "2026-07-16T09:41:02Z",
  "updatedAt": "2026-09-14T11:20:00Z",
  "permissions": ["*"]
}

The permissions array ​

permissions is what this caller may actually do, already resolved. Use it to decide which controls to draw instead of guessing from accountRole.

  • The account Owner gets exactly ["*"]. One element, the global wildcard, meaning everything.
  • A member gets their role's grant list verbatim: resource:action tokens and resource:* wildcards. Expand the wildcards yourself; the API does not do it for you.
  • An API key gets its own scope intersected with its creator's current grants, then capped to what a key may ever hold. A key never shows *, even one created by the Owner - it gets the expanded token list instead.

A member who somehow has no role at all comes back with "permissions": null rather than [], so check before you iterate.

A member on a custom role looks like this:

json
{
  "username": "bob",
  "accountRole": "member",
  "accountRoleId": "6a4d5b83b7006ccae6391841",
  "permissions": ["pods:*", "domains:read", "domains:update", "audit:read"]
}

accountRoleId is present only for members; the Owner has no role to point at. The tokens themselves come straight from the role, so GET /api/roles/ and this array always agree. The vocabulary is documented in Team & roles.

Do not treat this array as a security boundary - it is a hint for your UI. The API enforces the same grants on every call regardless of what you drew.

Get the account ​

http
GET /api/user/account

Account-level details: name, available credit, contact fields, alert settings, and your resource limits:

json
{
  "id": "6a4d5b83b7006ccae6391800",
  "name": "Alice's projects",
  "credit": 42.5,
  "email": "[email protected]",
  "phone": "+1 555 0100",
  "address": "123 Main St",
  "taxNumber": "GB123456789",
  "maxPods": 5,
  "maxDomains": 10,
  "maxNetworks": 10,
  "maxFirewalls": 10,
  "maxEmails": 0,
  "maxSnapshotsPerPod": 5,
  "maxBackupsPerPod": 5,
  "alertWebhookUrl": "https://example.com/hooks/microapps",
  "lowCreditThreshold": 5,
  "monthlySpendThreshold": 50,
  "createdAt": "2026-07-16T09:41:02Z",
  "updatedAt": "2026-09-14T11:20:00Z"
}

The max* fields are read-only. They show the ceilings on your account, and there is no customer endpoint to raise them - see Account limits. The numbers above are typical defaults for a fresh account (maxEmails stays 0 until email hosting is enabled on request); check your own account for the real ones. maxBackupsPerPod stops applying once paid backups start: it stays in the response, but nothing enforces it.

If your account is in dunning or suspended, this response also carries dunningStage, dunningStopAt, dunningDeleteAt, or suspended and suspendedAt. A healthy account omits all of them.

Change your password ​

http
POST /api/user/change-password
json
{
  "currentPassword": "Sup3rSecret",
  "password": "N3wSecret",
  "passwordConfirm": "N3wSecret"
}

The new password needs 8+ characters with an uppercase letter, a lowercase letter, and a digit. A wrong currentPassword returns 401.

Returns 204 No Content. Changing your password signs out every session - including the one that made this call - so log in again with the new password.

Update your profile ​

http
PUT /api/user/profile
json
{ "firstName": "Alice", "lastName": "Jones" }

Returns the updated user. Each name is capped at 60 characters.

Change your email ​

http
PUT /api/user/email
json
{ "email": "[email protected]", "currentPassword": "Sup3rSecret" }

We ask for your current password because an email change is sensitive. The new address has to be verified again - and until you click the verification link, calls under /api return 403. Watch for the email. Returns the updated user with isVerified back to false.

ProblemStatus
Wrong currentPassword401
email is already your address400
email belongs to another user409

Accept the current terms ​

http
POST /api/user/accept-terms

No body. Records that you accept the current version of the legal documents, and returns 204 No Content.

Compare the acceptedTermsVersion on your profile against termsVersion from GET /open/legal. If yours is behind, the documents have changed and the console puts a consent prompt in front of everything else; this is the call that prompt makes.

Update account contact details ​

http
PUT /api/user/account

Needs account:update.

json
{
  "name": "Alice's projects",
  "email": "[email protected]",
  "phone": "+1 555 0100",
  "address": "123 Main St",
  "taxNumber": "GB123456789"
}

These details show up on your invoices. This endpoint deliberately can not touch your resource limits - there is no self-service way to raise your own ceilings here. Returns the updated account.

Every field is optional and sending an empty string clears it. Lengths are capped at 120 (name), 40 (phone), 240 (address) and 60 (taxNumber) characters; go over on any of them and you get one 400: "One or more fields exceed the maximum length".

Alert webhook ​

http
PUT /api/user/alert-webhook

Needs account:update.

json
{ "url": "https://example.com/hooks/microapps" }

Where we POST account alerts, such as a low-credit warning. One URL per account. Send {"url": ""} to turn it off.

The URL must be http or https with a host, and it must resolve to a public address - a webhook aimed at private infrastructure is rejected with 400 ("Webhook must point to a public address").

json
{ "message": "Alert webhook updated" }

Alert thresholds ​

http
PUT /api/user/alert-thresholds

Needs account:update.

json
{ "lowCreditThreshold": 5.0, "monthlySpendThreshold": 50.0 }

These are alerts only. When your credit drops below lowCreditThreshold, or your month's usage crosses monthlySpendThreshold, we let you know (by email, and via your webhook if you set one). They never auto-charge a card or cap anything - MicroApps is prepaid, so nothing is billed unless you top up first.

Set either to 0 to disable that alert. Negative values return 400.

json
{ "message": "Alert thresholds updated" }

Example ​

bash
curl https://cloud-api.microapps.io/api/user/account \
  -H "Authorization: Bearer $MICROAPPS_API_KEY"

Check what the current credential is allowed to do:

bash
curl -s https://cloud-api.microapps.io/api/user/profile \
  -H "Authorization: Bearer $MICROAPPS_API_KEY" | jq .permissions

Built for the long tail.