Authentication — NuPaaS Docs
API reference

Authentication

The NuPaaS REST API is authenticated with a bearer token on every request. This page covers where to send requests, how to obtain a credential, what a credential is allowed to do, and how failures are reported.

Base URL

Every endpoint lives under a single versioned prefix. Paths in the rest of the API reference are written relative to it.

Base URL
https://platform.nupaas.com/api/v1

Responses use a consistent envelope. A single resource comes back as { "data": { … } }. A collection comes back as { "data": [ … ], "pagination": { … } }. Field names are snake_case throughout.

This is the same API the CLI uses. Anything you can do with platform you can do over HTTP, and the CLI is a useful way to check what a call should look like before you write the client.

Bearer tokens

Send the credential in an Authorization header. A request without one is rejected before any handler runs.

An authenticated request
curl https://platform.nupaas.com/api/v1/projects \
  -H "Authorization: Bearer plat_REPLACE_WITH_YOUR_KEY"

Two kinds of bearer token are accepted, and they behave differently in ways that matter when you choose one.

API keys

API keys begin with the prefix plat_. They are long-lived, may carry an expiry, and are the right credential for a script, a CI job or a server-side integration. Only a hash of the key is stored, so the full secret is shown exactly once — at creation — and cannot be recovered afterwards. If you lose it, revoke the key and mint a new one.

Session tokens

A token obtained through the interactive platform login browser flow is also accepted. It is convenient for exploration from your own machine, but it expires and it is tied to you personally, so it is a poor fit for automation.

A session token's permissions are derived from your actual membership record in the organization at the time of the request. Role claims carried inside the token itself are deliberately ignored. Removing someone from an organization therefore takes effect immediately, rather than lingering until their token expires.

Organization scope

This is the single most load-bearing fact about how the API scopes access, and it is the one most likely to send a client implementation in the wrong direction. There is no organization_id parameter to supply on list endpoints, no organization path segment, and no header to set. A key belongs to exactly one organization, and that binding is what determines which projects, deployments and databases the call can see.

The practical consequence: to operate across two organizations you need two credentials, and you select between them by choosing which key to send — not by changing anything in the request body.

Resource payloads do echo an organization back to you. A project resource, for example, carries an org_id field. That is output, not input; sending it on a write has no effect on which organization is used.

Scopes

Beyond the organization binding, each credential carries a set of scopes that decide which operations it may perform.

ParameterTypeDescription
readscopeList and retrieve resources. Sufficient for every GET endpoint — including reading credential and secret VALUES the key's role can see. Does not by itself withhold plaintext credentials; see the callout below.
writescopeCreate, update and delete resources — deployments, projects, environment variables, keys.
adminscopeSatisfies any read or write requirement on its own. Does not grant ops.
opsscopePlatform-fleet operations. Requires a platform-operator grant and is never implied by an organization role.

A request whose credential lacks the required scope is refused with 403 and a message naming the scope that was needed. admin is a superset of read and write, so a key holding only admin can still perform writes.

For a session token rather than an API key, scopes come from your organization role: owners and admins get read, write, admin; every other role, including member and viewer, gets read only.

Creating a key

Create a key from the CLI or by calling POST /keys. The secret appears once in the response and is never returned again.

Create a key from the CLI
platform keys create --name ci-deploy --scopes read,write --expires-in 90
Create a key over HTTP
curl -X POST https://platform.nupaas.com/api/v1/keys \
  -H "Authorization: Bearer plat_REPLACE_WITH_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-deploy","scopes":["read","write"],"expires_in_days":90}'
ParameterTypeDescription
namestringRequired. A label for the key, shown in listings.
rolestringOptional. One of owner, admin, member, viewer. Defaults to member.
scopesstring[]Optional. Defaults to an empty array.
expires_in_daysnumberOptional. Defaults to 0, meaning the key does not expire.

The response carries the created key as data plus the one-time value as a sibling secret field. The data.key_prefix field is a short non-secret fragment used to identify the key in listings and audit records.

A key cannot outrank its creator

Asking for a higher role fails with 403. It is not silently reduced to the highest role you are allowed — a rejection is louder than a clamp, and a clamp would leave you believing you hold permissions you do not. An admin requesting an owner key is refused; an admin requesting an admin key succeeds. Omitting role yields a member key.

The same principle applies to scopes when one key creates another: the requested scopes must be a subset of the scopes the creating key already holds. Viewers cannot create keys at all.

Revoke a key with DELETE /keys/{id}, or platform keys revoke --id …. Revocation takes effect on the next request.

Rate limits

Requests authenticated with an API key are limited to 1000 per minute per key, measured in a rolling window. Every response carries the current state of that window.

ParameterTypeDescription
X-RateLimit-LimitheaderThe ceiling for the window. Currently 1000.
X-RateLimit-RemainingheaderRequests still available in the current window.
X-RateLimit-ResetheaderUnix timestamp in seconds at which the window resets.

Exceeding the limit returns an error whose message states how many seconds remain until the window resets. The budget is per key, so splitting a noisy workload across several keys raises your effective throughput — and keeps one runaway job from starving the rest of your automation.

Errors

Errors return a JSON body with a stable machine-readable error code and a human-readable message. Branch on error; the wording of message may change.

Error body
{
  "error": "forbidden",
  "message": "Scope 'write' required for this operation"
}
ParameterTypeDescription
unauthorized401Missing, malformed, expired or revoked credential. Also returned when the token is valid but you hold no membership in its organization.
forbidden403Authenticated, but the credential lacks the required scope or role.
not_found404No such resource in your organization. Requesting another organization's resource looks the same as a resource that does not exist.
bad_request400The payload failed validation.
rate_limit_exceeded429Too many requests. Back off until X-RateLimit-Reset.
internal_error500Something failed server-side. Safe to retry with backoff.

Checking a credential

GET /auth/whoami is the cheapest way to confirm what a credential actually is. Reach for it first when a call is unexpectedly returning 403 — it tells you which organization you are in and which scopes you hold, which is usually the answer.

Identify the current credential
curl https://platform.nupaas.com/api/v1/auth/whoami \
  -H "Authorization: Bearer plat_REPLACE_WITH_YOUR_KEY"
Response
{
  "data": {
    "user_id": "usr_REPLACE",
    "org_id": "org_REPLACE",
    "org_role": "admin",
    "scopes": ["read", "write", "admin"]
  }
}
ParameterTypeDescription
user_idstringThe user the credential acts as.
org_idstringThe organization every request with this credential is scoped to.
org_rolestringThe organization role in effect.
scopesstring[]The scopes this credential holds.