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_slugandcontext.app_slug. - A
401from 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.readassets.writeproducts.readproducts.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.
- 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 publicauth.mddocument.
GET https://app.enad.io/.well-known/oauth-protected-resource
GET https://app.enad.io/.well-known/oauth-authorization-server- 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.
- 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.
- 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:
| 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:
- Fetch
auth.mdor follow the401discovery hint. - Read the protected-resource metadata and authorization-server metadata.
- Register with the user's verified email plus the target
team_slugandapp_slug. - Wait for the user to complete the email step and provide the OTP.
- Complete the claim and store the scoped API key with its expiry.
- Use that key for the narrow Enad operations the user actually allowed.
- 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_keycredentials 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.