AAuthfy Docs

External management API

These routes let an external application provision companies and add and update its company users. They live under /api/v1/external/manage/** and are separate from the read-only, token-scoped directory endpoints.

Scope. Category administration and user removal are deliberately not exposed externally — they are performed through the support console. Company provisioning (idempotent create + app link + first user) and user create and update are callable with an app key.

Authentication

All operations are recorded in the audit log.

Response envelope

Every management route returns the same three keys, on success and failure alike, so a client reads the outcome from the payload rather than the HTTP layer:

{ "status": 201, "message": "Created", "body": { "...": "..." } }
{ "status": 404, "message": "Company not found", "body": {} }

body is always present — an empty object when there is nothing to return. Bean-validation failures name the offending fields in message (for example "slug must not be blank; uuid must not be null"). The read routes under /api/v1/external/** are not enveloped and return bare payloads.

Provision a company (idempotent)

POST /api/v1/external/manage/companies

Creates a company, subscribes it to an application, and creates-and-links its first user in one call. The call is idempotent: repeating it with the same body never fails — whatever already exists is left in place and only the missing pieces are created.

Request (all six fields required; snake_case is canonical, and the camelCase aliases companyName, userName, userEmail, appId are also accepted):

{
  "uuid": "a3328ccf-0b64-4b2a-b735-886c330d57d5",
  "company_name": "Acme Ltd",
  "slug": "acme-ltd",
  "user_name": "John Doe",
  "user_email": "j.doe@acme.example",
  "app_id": "hrm"
}

Processing order:

  1. Validate the application public key.
  2. Resolve app_id to an application; an unknown value fails with 404 Application not found before anything is written.
  3. Match the company by uuid only:
    • Not found → create it with the supplied uuid, company_name and slug, immediately ACTIVE (no approval step). A slug already owned by a different company fails with 409 Company slug already exists.
    • Found → the existing company is left completely untouched (name, slug and status are not modified).
  4. Ensure the company is subscribed to the application. An existing ACTIVE link (including category-scoped access configuration) is left as-is.
  5. Look up user_email case-insensitively. A missing user is created email-verified with a generated password and sent the password-setup email; an existing user is reused without changes.
  6. Ensure the (user, company) membership. A missing membership is created with company role ADMIN (and triggers the membership-added email); an existing membership keeps its current role and status.

Responses — 201 Created when the company was created on this call, 200 Updated when it already existed; body.company is the company and body.user the company-user membership:

{ "status": 201, "message": "Created", "body": { "company": { "id": "...", "name": "Acme Ltd", "slug": "acme-ltd", "status": "ACTIVE" }, "user": { "userId": "...", "email": "j.doe@acme.example", "role": "ADMIN" } } }
{ "status": 200, "message": "Updated", "body": { "company": { "...": "..." }, "user": { "...": "..." } } }
{ "status": 404, "message": "Application not found", "body": {} }

Audit actions recorded for the steps that actually ran: CREATE_COMPANY, SET_COMPANY_STATUS, SUBSCRIBE_COMPANY_APPLICATION, CREATE_USER, ATTACH_USER_COMPANY.

Create or link a company user

POST /api/v1/external/manage/users

Body fields: companyId (required), email (required), name (required), password?, systemRole?, role?, emailVerified?.

Minimal request:

{
  "companyId": "a3328ccf-0b64-4b2a-b735-886c330d57d5",
  "email": "j.doe@acme.example",
  "name": "John Doe"
}

Processing order:

  1. Validate the application public key.
  2. companyId must identify an existing company; otherwise 404 Company not found.
  3. Look up email case-insensitively.
  4. An existing user is linked to the company without changing its profile, credentials, or verification state.
  5. A missing user is created, sent its password-setup email, and then linked.
  6. After attachment, the user is emailed that they were added to the named company.

Returns 201 with the company-user membership in body. Notes:

Update a company user

PUT /api/v1/external/manage/users

Body fields: companyId (required), userId (required), name?, phoneNumber?, avatarUrl?, locale?, timezone?, status?, role?.

{
  "companyId": "a3328ccf-0b64-4b2a-b735-886c330d57d5",
  "userId": "5f5e1a3c-1111-2222-3333-444455556666",
  "name": "John D.",
  "role": "ADMIN"
}

Updates the profile fields provided; pass role to change the company role. Returns 200 with the membership in body.

Removed routes

These were withdrawn from the external API; use the support console instead. Calling them returns 404:

MethodPath
PUT / DELETE/api/v1/external/manage/companies
POST / PUT / DELETE/api/v1/external/manage/categories
DELETE/api/v1/external/manage/users
PUT/api/v1/external/manage/users/categories

Detachment (suspending a membership) is performed by your Authfy administrator; it suspends rather than deletes, so a membership can be reactivated later, and the user is notified by email.