Managing OAuth clients

Organization administrators can create and manage OAuth clients for their organization directly via the REST API.

All endpoints in this reference require an organization-admin access token in the Authorization header. See Obtaining a ClientID for your Application for background on Client IDs.

Anonymous requests return 401 Unauthorized; requests from non-admin users return 403 Forbidden. A request for a client that belongs to a different organization returns 404 Not Found, so the existence of other organizations’ clients is never revealed.

The response examples on this page show the main fields; actual responses can include additional fields.

Create a Public Client

Creates a public OAuth client (mobile, desktop, SPA, CLI). Public clients use authorization_code with PKCE and have no client secret.

POST "https://account.ezeep.com/v1/oauth-clients/public/"
Attribute Type Required Description
name string yes Human-readable name shown on the consent screen. Must be unique within your organization, ignoring case.
redirect_urls string array yes Allowed redirect URIs. Max 20. Each URL must be HTTPS, http://localhost, or a reverse-DNS native scheme (e.g. com.example.app://cb). No wildcards, no fragments, no userinfo.
external_description string no Description shown on the consent screen.
allowed_scopes string no Space-separated scopes. See Validation Rules below.

Public clients support the authorization_code grant only and must use PKCE with the S256 method; no client_secret is issued. The grant_types field is not accepted in the request body for public clients.

Example Request

curl -X POST "https://account.ezeep.com/v1/oauth-clients/public/" \
     --header "Content-Type: application/json" \
     --header "Authorization: Bearer <access_token>" \
     --data '{
         "name": "Acme Print SPA",
         "redirect_urls": ["https://app.acme.example/oauth/callback"],
         "external_description": "Acme document printing for the web",
         "allowed_scopes": "printing.print printing.documents.upload printing.printers.readonly openid profile"
     }'

Example Response (201 Created)

{
    "client_id": "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789AbCd",
    "name": "Acme Print SPA",
    "external_description": "Acme document printing for the web",
    "redirect_urls": ["https://app.acme.example/oauth/callback"],
    "allowed_scopes": ["printing.print", "printing.documents.upload", "printing.printers.readonly", "openid", "profile"],
    "grant_types": ["authorization_code"],
    "client_type": "public"
}

Create a Confidential Client

Creates a confidential OAuth client (server-side backend that can protect a secret). Confidential clients receive a client_secret that is shown once in this response and cannot be retrieved later. Store it in a secure backend immediately. If it is lost, delete the client and create a new one.

POST "https://account.ezeep.com/v1/oauth-clients/confidential/"
Attribute Type Required Description
name string yes Human-readable name shown on the consent screen. Must be unique within your organization, ignoring case.
grant_types string array yes Exactly one of authorization_code or pairing_code.
redirect_urls string array conditional Required for authorization_code. May be empty for pairing-only clients. Same validation as public clients: max 20; HTTPS, http://localhost, or reverse-DNS native scheme; no wildcards, no fragments, no userinfo.
external_description string no Description shown on the consent screen.
allowed_scopes string no Space-separated scopes. See Validation Rules below.

Confidential clients accept exactly one grant type per client. Admins who need both authorization_code and pairing_code should register two clients.

Example Request

curl -X POST "https://account.ezeep.com/v1/oauth-clients/confidential/" \
     --header "Content-Type: application/json" \
     --header "Authorization: Bearer <access_token>" \
     --data '{
         "name": "Acme Print Backend",
         "grant_types": ["authorization_code"],
         "redirect_urls": ["https://backend.acme.example/ezeep/callback"],
         "external_description": "Acme document printing backend",
         "allowed_scopes": "printing.print printing.documents.upload printing.printers.readonly openid profile"
     }'

Example Response (201 Created)

{
    "client_id": "ZyXwVuTsRqPoNmLkJiHgFeDcBa9876543210zYxW",
    "client_secret": "s3cr3t_shown_once_store_immediately_a1b2c3d4e5f6g7h8i9j0",
    "name": "Acme Print Backend",
    "external_description": "Acme document printing backend",
    "redirect_urls": ["https://backend.acme.example/ezeep/callback"],
    "allowed_scopes": ["printing.print", "printing.documents.upload", "printing.printers.readonly", "openid", "profile"],
    "grant_types": ["authorization_code"],
    "client_type": "confidential"
}

Validation Rules

The following rules apply to both public and confidential clients.

Redirect URLs

  • Must be HTTPS, http://localhost (any port and path), or a reverse-DNS native scheme (e.g. com.example.app://callback).
  • Wildcards, URL fragments (#...), and userinfo (user:pass@...) are rejected.
  • Maximum of 20 URLs per client.

Scopes

  • On input: space-separated string, maximum length 2048 characters. On output: JSON array of scope strings.
  • Only defined ezeep scopes from the printing, accounts, reporting, azure and webhooks families are accepted, plus openid, profile and email.
  • Any other scope is rejected with 400 Bad Request and the message “One or more requested scopes are not permitted via API.”
  • Tokens issued to the client only carry scopes listed in allowed_scopes; any other requested scope is dropped without an error. A client created without allowed_scopes issues tokens without any scopes, so list every scope your application will request.
  • For a printing integration, use printing.print printing.documents.upload printing.printers.readonly openid profile, the same set the MCP self-service prompt uses. If your application also calls the reporting or webhooks APIs, add reporting or webhooks; for read-only access, reporting.reports.readonly or webhooks.readonly.

Consent screen

The ezeep consent screen is always shown to end users during the authorization flow for clients created through this API.

Refresh-token rotation

Clients created through this API have refresh-token rotation enabled by default: each refresh_token is single-use, and the token-exchange response returns a new refresh_token that must replace the stored value.

Rate and count limits

Two limits apply at creation. Both return 429 Too Many Requests.

  • Per admin: up to 10 create requests per hour, including requests rejected by validation. Exceeding this returns 429 with a Retry-After header.
  • Per organization: up to 100 OAuth clients created through this API. Exceeding this returns 429 immediately; delete a client you no longer need to free a slot.

List Clients

Lists OAuth clients owned by the caller’s organization. Results are ordered by created descending (newest first) and then by client_id. Clients that were not created through this API are not included. Results are paginated with the limit and offset query parameters: 20 per page by default, up to 100.

GET "https://account.ezeep.com/v1/oauth-clients/"

Example Request

curl -X GET "https://account.ezeep.com/v1/oauth-clients/" \
     --header "Authorization: Bearer <access_token>"

Example Response

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "client_id": "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789AbCd",
            "name": "Acme Print SPA",
            "client_type": "public",
            "grant_types": ["authorization_code"]
        },
        {
            "client_id": "ZyXwVuTsRqPoNmLkJiHgFeDcBa9876543210zYxW",
            "name": "Acme Print Backend",
            "client_type": "confidential",
            "grant_types": ["authorization_code"]
        }
    ]
}

Client secrets are never returned by this endpoint (or any other endpoint after creation).

Retrieve a Client

Returns the current configuration of a single client.

GET "https://account.ezeep.com/v1/oauth-clients/<client_id>/"

Example Request

curl -X GET "https://account.ezeep.com/v1/oauth-clients/aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789AbCd/" \
     --header "Authorization: Bearer <access_token>"

Update a Client

Partially updates a client. Only the fields below may be changed; client_type and grant_types are immutable and must be set correctly at creation. Unknown fields in the request body are rejected with 400 Bad Request.

PATCH "https://account.ezeep.com/v1/oauth-clients/<client_id>/"
Attribute Type Required Description
name string no Human-readable name shown on the consent screen. Must be unique within your organization, ignoring case.
external_description string no Description shown on the consent screen.
redirect_urls string array no Same validation rules as at creation.
allowed_scopes string no Space-separated string on input; same validation as at creation. The response body returns allowed_scopes as a JSON array.

Example Request

curl -X PATCH "https://account.ezeep.com/v1/oauth-clients/aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789AbCd/" \
     --header "Content-Type: application/json" \
     --header "Authorization: Bearer <access_token>" \
     --data '{
         "redirect_urls": [
             "https://app.acme.example/oauth/callback",
             "https://staging.acme.example/oauth/callback"
         ]
     }'

Delete a Client

Deletes a client immediately. Existing access tokens issued to the client remain valid until they expire, but no new tokens can be issued and no refresh will succeed. There is no soft-delete window. A subsequent GET for the same client_id returns 404.

DELETE "https://account.ezeep.com/v1/oauth-clients/<client_id>/"

Example Request

curl -X DELETE "https://account.ezeep.com/v1/oauth-clients/aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789AbCd/" \
     --header "Authorization: Bearer <access_token>"

Returns 204 No Content on success.

Notes

MCP self-service. The ezeep MCP server’s register_oauth_client tool calls the public-client endpoint on behalf of an organization administrator; see the MCP self-service prompt for a conversational alternative to the raw endpoint.

Clients not visible to this API. Clients that were not created through this API are not listed and return 404 on direct access. They must continue to be managed by ezeep; contact the ezeep team via the API Contact Form.