Authorization for api.post.m7.org
api.post.m7.org has two authorization models. Member routes use an M7
access token and scope stored resources to the resolved local principal. Proof
routes do not use a Bearer token; they use an active proof job identifier and,
when verifying or consuming a challenge, the one-time secret delivered for
that job.
The API base URL is:
https://api.post.m7.org
Supported credentials
| Credential | Format and transport | Where it applies | Authorization boundary |
|---|---|---|---|
| M7 access token | Signed compact JWT in Authorization: Bearer ACCESS_TOKEN |
/v1/members/* |
A valid token must resolve to an active local principal with the user, admin, or root role. Every member resource is scoped to that principal's local ID. |
| Proof job ID | UUID in the JSON field job or job_id |
/v1/proof/get, /v1/proof/send, /v1/proof/verify, and /v1/proof/consume |
The job must exist, be active, and use the single or any proof policy. The ID alone can inspect the masked proof state or request a resend, so treat it as sensitive. |
| Proof code | Six decimal digits in the JSON field secret or code; select kind: "code" |
/v1/proof/verify and /v1/proof/consume |
Must match the current active code for the supplied job. The default lifetime is 30 minutes; use the returned challenge.expires_at as authoritative. |
| Proof-token secret | 64 hexadecimal characters in the JSON field secret or token; select kind: "token" |
/v1/proof/verify and /v1/proof/consume |
Must match the current active token-kind challenge for the supplied job. The default lifetime is 60 minutes; use the returned challenge.expires_at as authoritative. |
ID tokens, refresh tokens, OAuth client secrets, proof codes, and proof-token secrets do not replace an M7 access token on member routes. Conversely, a Bearer token does not replace the job ID and one-time secret required by the proof flow.
Obtain an M7 access token
This service does not issue or refresh M7 access tokens. Obtain one through the M7 SSO API using the flow that matches the caller:
| Caller | M7 SSO flow |
|---|---|
| Browser, native application, or server-rendered application acting for a user | Authorization code with PKCE |
| CLI, TV, or other input-constrained application | Device authorization |
| Confidential service acting as itself | client_credentials |
Use the OAuth client and audience configured for this API. User-authorized
flows may return a refresh package; send that package only to the M7 SSO token
endpoint. A client_credentials token has no refresh token and must be
reissued when it expires.
Never collect an M7 password in an integrating application. Never send a
client secret, authorization code, refresh token, or refresh-package binding
value to api.post.m7.org.
Send a Bearer request
The public member-route contract is the Bearer authorization scheme:
curl -sS https://api.post.m7.org/v1/members/job/list \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{}'
Use the scheme name Bearer, followed by one space and the access token. Do
not put an access token in a URL, query string, JSON body, or cookie, and do
not depend on compatibility token headers.
M7 access tokens are JWTs, but clients must treat them as opaque secrets. Decoded claims are not proof that the token is accepted or that the caller is authorized for a resource.
The service currently validates:
- the compact JWT structure and signature;
- the
RS512signature profile; - the
id.m7.orgtoken issuer; - an HTTPS signing-certificate source hosted by
id.m7.org; and - the token's
nbf,iat, andexptime claims when present.
The current service configuration does not require an audience match. Clients must still request and use a token intended for this API; the absence of a local audience check is not permission to reuse credentials across services.
Principal and ownership rules
After token validation, the service resolves the token identity to a local principal. A newly observed supported identity can be registered as an active principal; subsequent authorization uses the current local principal record.
Member routes allow these local roles:
| Local role | Member-route behavior |
|---|---|
user |
May create and operate only on resources owned by that principal. |
admin |
May call the same member routes, still scoped to that principal. |
root |
May call the same member routes, still scoped to that principal. |
The API derives owner from the resolved local principal ID and overwrites
caller input. Supplying another principal's UUID, knowing another resource
UUID, or holding an elevated role does not make a member route operate on
another principal's resources.
A valid token can still be denied when the local principal is missing, inactive, or not assigned an allowed role. Always use the resource IDs returned for the same authenticated principal.
DPoP status
DPoP is not part of the supported api.post.m7.org authorization contract.
The service validates the Bearer access token but does not validate a DPoP
proof's signature, HTTP method and URL binding, access-token binding,
freshness, unique identifier, or replay state. CORS allowance or header
parsing for DPoP does not change that contract.
Send the Bearer request described above. Do not rely on a DPoP proof to authorize a member request.
Use the proof credentials
An authenticated principal creates and activates a proof job through the member API. Activation sends the initial challenge to the selected email method and returns the job record to the owning application. The application must pass the job UUID to the intended recipient flow through a trusted channel. The recipient receives the service-generated proof code or proof-token secret by email.
Proof routes do not require a Bearer token. Their access boundary is the specific active job and its current challenge; they do not create a general principal session.
Inspect the current proof state
/v1/proof/get requires the job ID. It returns masked contact methods and the
current challenge's kind, status, and expiry without returning the secret:
curl -sS https://api.post.m7.org/v1/proof/get \
-H 'Content-Type: application/json' \
-d '{
"job": "JOB_UUID",
"kind": "code"
}'
Omitting kind selects code. Use kind: "token" when the job was armed
with a proof-token secret.
Verify or consume a code
/verify checks the code and marks it verified without completing the job.
/consume checks the code, consumes it, marks the email method verified, and
completes the job. Most integrations should consume when successful:
curl -sS https://api.post.m7.org/v1/proof/consume \
-H 'Content-Type: application/json' \
-d '{
"job": "JOB_UUID",
"kind": "code",
"secret": "123456"
}'
code is an accepted alias for secret when the selected kind is code.
Verify or consume a proof-token secret
Use the same routes with kind: "token":
curl -sS https://api.post.m7.org/v1/proof/consume \
-H 'Content-Type: application/json' \
-d '{
"job_id": "JOB_UUID",
"kind": "token",
"secret": "PROOF_TOKEN"
}'
token is an accepted alias for secret and also selects the token kind when
kind is omitted. This proof-token secret is unrelated to an M7 access token
and must never be sent in the Authorization header.
Resend and terminal states
/v1/proof/send requires the job ID and an eligible method_id returned by
the proof state. It generates and emails a new challenge. Do not supply
secret, code, or token to this route.
Issuing a new challenge revokes the previous active challenge and makes the selected method current. A successful consume completes the job and revokes sibling active credentials. Expired, consumed, revoked, or replaced credentials cannot be refreshed or reused; send a new challenge while the job is active, or have the owning application start a new job after a terminal state.
Access-token expiry, refresh, and revocation
- Stop using an access token when its reported lifetime ends. Obtain a new token or refresh package through M7 SSO; this API does not extend token lifetimes.
- Send refresh tokens and their
binding_chainandbinding_linkvalues only to the issuing M7 SSO token endpoint with the owning OAuth client's required authentication. - Revoke an eligible credential through M7 SSO and discard every local copy.
api.post.m7.orgvalidates access-token JWTs locally and does not perform online introspection for each request, so clients must not keep using a revoked token while waiting for its signed lifetime to end. - Disabling or restricting the local principal can deny later member requests even while the JWT remains cryptographically valid.
Common failures
Member authorization failures use HTTP 401 for a missing principal and
HTTP 403 for other access denials. The JSON body has status: 0, a numeric
code, a diagnostic comment, and a machine-oriented reason.
| Failure | Typical cause | Client action |
|---|---|---|
Missing token with reason: "missing_principal" |
The Bearer header is absent | Add a valid M7 access token to the Authorization header. |
Invalid token with reason: "missing_principal" |
The JWT is malformed, expired, has an invalid signature, or fails issuer or certificate validation | Discard it and obtain a new access token through M7 SSO. |
principal_role_denied |
The active principal does not have an allowed member role | Use an authorized principal; do not retry with another resource ID. |
| Principal inactive or unavailable | The token identity does not resolve to an active local principal | Reauthenticate; if the failure persists, restore the principal instead of retrying blindly. |
Invalid job or Job not found |
The proof request has a malformed or unknown job UUID | Use the exact job ID supplied by the owning application. |
Job is not active or Job does not support proof |
The job is draft, completed, failed, cancelled, or uses proof_policy: "none" |
Stop the proof attempt and return to the owning application. |
Proof challenge is not armed |
The selected kind has no current active, unexpired challenge | Inspect the proof state and resend if the job remains active. |
Credential not found |
The code or proof-token secret is incorrect | Ask the recipient to re-enter the current delivered secret; do not expose which part mismatched. |
Forced secret is not allowed on send |
A resend request included secret, code, or token |
Remove the secret and let the service generate the replacement. |
Security checklist
- Use TLS and the exact
https://api.post.m7.orgproduction host. - Keep access tokens, proof job IDs, proof secrets, refresh packages, and OAuth client secrets out of source control, logs, analytics, URLs, and error reports.
- Store long-lived OAuth material in an application-owned secure store.
- Share a proof job ID only with the intended recipient flow; possession can inspect masked state and trigger a resend.
- Accept a proof secret only in the JSON request body over HTTPS. Never place it in a link, query string, cookie, or authorization header.
- Treat
challenge.expires_atand terminal job state as authoritative. Do not retry expired, consumed, revoked, or replaced credentials. - Use API responses and current local authorization state for access decisions; do not authorize from decoded JWT claims in client code.
Related documentation: