Enad app auth.md

Guide for Enad app auth.md discovery, email-claimed registration, OTP completion, and scoped API-key usage.

Register through auth.md when an agent or operator needs a scoped credential for one Enad app context.

Keep the registration scope narrow: discover the resource, register with an existing app-user email, complete the OTP claim, use the returned scoped API key, and restart discovery on 401.

Live discovery

On app.enad.io, fetch /auth.md, /.well-known/oauth-protected-resource, and /.well-known/oauth-authorization-server. Discovery is origin-relative, so use the same origin for metadata, registration, claim completion, and later API calls.

If a protected route returns 401, read the WWW-Authenticate header. Follow its resource_metadata value instead of guessing the next step.

What v1 supports

The current Enad app auth.md flow exposes one bounded registration path:

  • The identity type is identity_assertion.
  • The assertion type is verified_email.
  • The credential type is api_key.
  • Each registration names one app context through context.team_slug and context.app_slug.
  • A 401 from a previously issued credential means restart discovery.

This v1 flow is email-claimed and user-bound. Use the email address of an existing Enad app user for the target context.

Supported v1 scopes

Enad grants scopes from the matched user's existing app permissions only. A scope is returned only when the matched role has the full permission set behind that scope. The v1 scope set is:

  • assets.read
  • assets.write
  • products.read
  • products.write

Do not assume broader tenant-admin or system-wide access. If the user does not already hold the required app permissions, registration will not mint those scopes.

Registration flow

Use this order every time. Discover against the live origin, create a pending registration, complete the user-present OTP step, then store the returned credential with its expiry.

  1. Discover the resource metadata. Fetch both metadata routes before registration; the protected-resource document identifies the resource and scopes, while the authorization-server document advertises agent_auth, the register URI, the claim URI, and the public auth.md document.
GET https://app.enad.io/.well-known/oauth-protected-resource
GET https://app.enad.io/.well-known/oauth-authorization-server
  1. Create a pending registration. Call the register endpoint with the user's verified email, the requested credential type, and the explicit team/app context:
POST https://app.enad.io/agent/auth
Content-Type: application/json
{
  "type": "identity_assertion",
  "assertion_type": "verified_email",
  "assertion": "user@example.com",
  "requested_credential_type": "api_key",
  "context": {
    "team_slug": "enadhq",
    "app_slug": "demo-storefront"
  }
}

A successful registration returns registration_id, registration_type: "email-verification", claim_token, claim_token_expires, claim_url, and post_claim_scopes.

No credential is issued yet. Enad emails the user a claim link for the hosted OTP step.

  1. Complete the OTP claim. Ask the user to open the claim email, read the six-digit OTP, and provide that code to the agent or operator. Then complete the claim:
POST https://app.enad.io/agent/auth/claim/complete
Content-Type: application/json
{
  "claim_token": "clm_...",
  "otp": "123456"
}

A successful claim returns status: "claimed", credential_type: "api_key", the credential, credential_expires, and granted scopes. Store the credential and expiry together before making API calls.

  1. Use the credential. Present the returned API key as a bearer token:
Authorization: Bearer <credential>

If a previously working credential starts returning 401, drop it and restart from discovery instead of retrying with stale state.

Failure and retry guidance

Use these error outcomes to choose the next step:

CodeMeaningNext step
invalid_requestThe request body does not match the v1 schema.Rebuild the request from the schema shown above.
missing_app_accessThe email does not match an existing user in the requested app context, or the matched user has no v1 scopes available.Ask for a different app context or an existing app-user email with the required permissions.
unsupported_credential_typeThe request asked for something other than api_key.Retry with api_key.
claim_not_requiredThe caller tried to start an anonymous or separate claim step.Use POST /agent/auth; the v1 email-verification flow sends the claim email during registration.
invalid_claim_tokenThe claim token is missing, invalid, or unknown.Restart at registration.
otp_invalidThe six-digit code did not match.Ask the user to re-read the OTP and retry.
otp_expiredThe OTP timed out.Restart at registration for a fresh email.
claim_expiredThe whole registration expired.Restart at registration.
previously_claimedThe claim was already completed.Restart registration if a fresh key is needed.
rate_limitedToo many registration attempts came from the same IP.Back off and retry later.
email_delivery_unavailableThe claim-email delivery path is not configured for the environment.Treat the environment as unavailable for public registration until delivery is configured.

Operator usage example

Use this flow when an operator needs to connect to one Enad app on a user's behalf:

  1. Fetch auth.md or follow the 401 discovery hint.
  2. Read the protected-resource metadata and authorization-server metadata.
  3. Register with the user's verified email plus the target team_slug and app_slug.
  4. Wait for the user to complete the email step and provide the OTP.
  5. Complete the claim and store the scoped API key with its expiry.
  6. Use that key for the narrow Enad operations the user actually allowed.
  7. If Enad later returns 401, discard the key and restart the flow instead of silently reusing stale credentials.

Boundaries

Keep these constraints explicit:

  • v1 supports api_key credentials only.
  • The user must already exist in the requested app context.
  • Scopes come from existing Enad app permissions, not from free-form agent requests.
  • Anonymous-start claims are not part of this v1 flow.
  • Agents do not call a dedicated public revoke endpoint in this v1 flow.

Operators can inspect resulting registrations inside Enad and revoke the issued API key through the normal product surfaces after sign-in.

Enad app auth.md

# Enad app auth.md Register through `auth.md` when an agent or operator needs a scoped credential for one Enad app context. Keep the registration scope narrow: discover the resource, register with an existing app-user email, complete the OTP claim, use the returned scoped API key, and restart discovery on `401`. ## Live discovery On `app.enad.io`, fetch `/auth.md`, `/.well-known/oauth-protected-resource`, and `/.well-known/oauth-authorization-server`. Discovery is origin-relative, so use the same origin for metadata, registration, claim completion, and later API calls. If a protected route returns `401`, read the `WWW-Authenticate` header. Follow its `resource_metadata` value instead of guessing the next step. ## What v1 supports The current Enad app `auth.md` flow exposes one bounded registration path: - The identity type is `identity_assertion`. - The assertion type is `verified_email`. - The credential type is `api_key`. - Each registration names one app context through `context.team_slug` and `context.app_slug`. - A `401` from a previously issued credential means restart discovery. This v1 flow is email-claimed and user-bound. Use the email address of an existing Enad app user for the target context. ## Supported v1 scopes Enad grants scopes from the matched user's existing app permissions only. A scope is returned only when the matched role has the full permission set behind that scope. The v1 scope set is: - `assets.read` - `assets.write` - `products.read` - `products.write` Do not assume broader tenant-admin or system-wide access. If the user does not already hold the required app permissions, registration will not mint those scopes. ## Registration flow Use this order every time. Discover against the live origin, create a pending registration, complete the user-present OTP step, then store the returned credential with its expiry. 1. Discover the resource metadata. Fetch both metadata routes before registration; the protected-resource document identifies the resource and scopes, while the authorization-server document advertises `agent_auth`, the register URI, the claim URI, and the public `auth.md` document. ```http GET https://app.enad.io/.well-known/oauth-protected-resource GET https://app.enad.io/.well-known/oauth-authorization-server ``` 2. Create a pending registration. Call the register endpoint with the user's verified email, the requested credential type, and the explicit team/app context: ```http POST https://app.enad.io/agent/auth Content-Type: application/json ``` ```json { "type": "identity_assertion", "assertion_type": "verified_email", "assertion": "user@example.com", "requested_credential_type": "api_key", "context": { "team_slug": "enadhq", "app_slug": "demo-storefront" } } ``` A successful registration returns `registration_id`, `registration_type: "email-verification"`, `claim_token`, `claim_token_expires`, `claim_url`, and `post_claim_scopes`. No credential is issued yet. Enad emails the user a claim link for the hosted OTP step. 3. Complete the OTP claim. Ask the user to open the claim email, read the six-digit OTP, and provide that code to the agent or operator. Then complete the claim: ```http POST https://app.enad.io/agent/auth/claim/complete Content-Type: application/json ``` ```json { "claim_token": "clm_...", "otp": "123456" } ``` A successful claim returns `status: "claimed"`, `credential_type: "api_key"`, the `credential`, `credential_expires`, and granted `scopes`. Store the credential and expiry together before making API calls. 4. Use the credential. Present the returned API key as a bearer token: ```http Authorization: Bearer ``` If a previously working credential starts returning `401`, drop it and restart from discovery instead of retrying with stale state. ## Failure and retry guidance Use these error outcomes to choose the next step: | Code | Meaning | Next step | | --- | --- | --- | | `invalid_request` | The request body does not match the v1 schema. | Rebuild the request from the schema shown above. | | `missing_app_access` | The email does not match an existing user in the requested app context, or the matched user has no v1 scopes available. | Ask for a different app context or an existing app-user email with the required permissions. | | `unsupported_credential_type` | The request asked for something other than `api_key`. | Retry with `api_key`. | | `claim_not_required` | The caller tried to start an anonymous or separate claim step. | Use `POST /agent/auth`; the v1 email-verification flow sends the claim email during registration. | | `invalid_claim_token` | The claim token is missing, invalid, or unknown. | Restart at registration. | | `otp_invalid` | The six-digit code did not match. | Ask the user to re-read the OTP and retry. | | `otp_expired` | The OTP timed out. | Restart at registration for a fresh email. | | `claim_expired` | The whole registration expired. | Restart at registration. | | `previously_claimed` | The claim was already completed. | Restart registration if a fresh key is needed. | | `rate_limited` | Too many registration attempts came from the same IP. | Back off and retry later. | | `email_delivery_unavailable` | The claim-email delivery path is not configured for the environment. | Treat the environment as unavailable for public registration until delivery is configured. | ## Operator usage example Use this flow when an operator needs to connect to one Enad app on a user's behalf: 1. Fetch `auth.md` or follow the `401` discovery hint. 2. Read the protected-resource metadata and authorization-server metadata. 3. Register with the user's verified email plus the target `team_slug` and `app_slug`. 4. Wait for the user to complete the email step and provide the OTP. 5. Complete the claim and store the scoped API key with its expiry. 6. Use that key for the narrow Enad operations the user actually allowed. 7. If Enad later returns `401`, discard the key and restart the flow instead of silently reusing stale credentials. ## Boundaries Keep these constraints explicit: - v1 supports `api_key` credentials only. - The user must already exist in the requested app context. - Scopes come from existing Enad app permissions, not from free-form agent requests. - Anonymous-start claims are not part of this v1 flow. - Agents do not call a dedicated public revoke endpoint in this v1 flow. Operators can inspect resulting registrations inside Enad and revoke the issued API key through the normal product surfaces after sign-in. ## Related routes - [Agent guides](/ai) - [Agent quickstart](/ai/agent-quickstart) - [Integrations](/integrations)