Skip to content

Admin Resources

This page documents backend platform-management resources: role- and permission-based access control, home banners for in-app announcements, a media library for uploaded assets, and a per-user notification system. These are relevant mainly if you’re building deeper admin/dashboard tooling on top of Wetel — see Admin API Overview for the broader admin surface. If you’re only integrating a runtime agent (starting sessions, sending messages), you likely don’t need this page — see API Reference Overview for the full index.

Every request must include the x-huat-platform: customer header, and every operation on this page requires a JWT (Authorization: Bearer <token>), obtained via login. None of these operations accept an API key.

Roles are named groups of permission slugs, used to control which portal/admin operations a user can perform. RoleEntity and PermissionEntity are landlord-level (not filtered by tenant row-by-row), but every mutation still requires an authenticated, tenant-resolved caller.

Returns every active Permission.

Auth required: JWT.

Arguments: None.

Returns: [Permission!]!

FieldTypeNotes
idInt!
nameString!
slugString!Kebab-case, e.g. create-agent. Used in CreateOneRoleInput.slugs/UpdateRoleInput.slugs
moduleModuleType!HOME_BANNER, NOTIFICATION, PERMISSION, PORTAL_USER, ROLE, TENANT, or USER
descriptionString
createdAt / updatedAtDateTime!
query GetAllPermissions {
getAllPermissions {
id
slug
module
}
}

Fetches a single Permission by id.

Auth required: JWT.

Arguments:

ArgumentTypeRequired
idInt!Yes

Returns: Permission! (see shape above)

Lists permissions, filterable/sortable (no pagination — this is a small, complete list, not a Relay connection).

Auth required: JWT.

Arguments:

ArgumentTypeDefault
filterPermissionFilter!{}
sorting[PermissionSort!]![]

PermissionFilter supports id, slug, module, createdAt, updatedAt, plus and/or composition — standard comparison operators (eq, in, like, etc.) per field.

Returns: [Permission!]!

Fetches one role or a paginated list of roles. Despite the Portal naming, these live on the main API and are gated by a regular JWT — they are the admin/portal-side access-control surface, not part of the customer-facing agent/session schema.

Auth required: JWT.

getPortalRole arguments: id: Int! → returns Role!.

getPortalRoles arguments:

ArgumentTypeDefault
filterRoleFilter!{}
pagingOffsetPaging!{ limit: 10 }
sorting[RoleSort!]![]

Returns RoleConnection!{ nodes: [Role!]!, pageInfo: OffsetPageInfo!, totalCount: Int! } (offset pagination — pass { limit, offset } in paging, not a cursor).

Role fields:

FieldTypeNotes
idInt!
nameString!
descriptionString
permissions[Permission!]Accepts its own nested filter/sorting args
totalPortalUserIntReserved for a separate admin-portal service; always 0 from this API
createdAt / updatedAtDateTime!

Creates a role from a set of permission slugs.

Auth required: JWT.

Arguments:

ArgumentTypeRequired
inputCreateOneRoleInput!Yes

CreateOneRoleInput:

FieldTypeNotes
nameString!
descriptionString!
slugs[String!]!Must resolve to at least one existing Permission, or the mutation throws

Returns: Role!

mutation CreateOneRole($input: CreateOneRoleInput!) {
createOneRole(input: $input) {
id
name
permissions {
slug
}
}
}
{
"input": {
"name": "Support Lead",
"description": "Manage banners and notifications",
"slugs": ["view-home-banner", "delete-home-banner"]
}
}

Auth required: JWT.

Arguments:

ArgumentTypeRequired
inputUpdateOneRoleInput!Yes

UpdateOneRoleInput: { id: Int!, update: UpdateRoleInput! }. UpdateRoleInput fields (name, description, slugs) are all optional — send only what is changing. If slugs is provided it replaces the role’s permission set entirely and must resolve to at least one existing permission.

Returns: Role!

Soft-deletes a role.

Auth required: JWT.

Arguments: id: Int!

Returns: Boolean!

mutation DeleteOneRole($id: Int!) {
deleteOneRole(id: $id)
}

Home banners are scheduled announcements shown in-app. There are two separate read surfaces:

Auth required: None beyond the standard platform header — this is intentionally a public, unauthenticated read.

Arguments:

ArgumentTypeDefault
filterHomeBannerFilter!{}
pagingOffsetPaging!{ limit: 10 }
sorting[HomeBannerSort!]![{ direction: DESC, field: createdAt }] (server also always applies a priority: ASC sort ahead of your filter)

Returns: HomeBannerOffsetConnection!{ nodes: [HomeBanner!]!, pageInfo: OffsetPageInfo!, totalCount: Int! }

HomeBanner fields:

FieldTypeNotes
idInt!
titleString!
descriptionString!
actionsString!
urlString!
isInternalLinkBoolean!
priorityFloat!Lower sorts first
statusGeneralStatusType!DRAFT or PUBLISHED
startDate / endDateDateTime
image / mobileImageMediaLibraryNullable — resolved from the stored imageId/mobileImageId
createdAt / updatedAtDateTime!

getPortalHomeBanner / getPortalHomeBanners

Section titled “getPortalHomeBanner / getPortalHomeBanners”

Admin-side equivalents of the above, including unpublished and out-of-window banners. Same HomeBanner shape.

Auth required: JWT, with the HOME_BANNER / VIEW access-control permission.

getPortalHomeBanner arguments: id: Int! → returns HomeBanner!.

getPortalHomeBanners arguments: same shape as getHomeBanners (filter, paging, sorting, default sort [] — no forced ordering). Returns HomeBannerConnection!.

Auth required: JWT, with the HOME_BANNER / CREATE permission.

Arguments:

ArgumentTypeRequired
inputCreateHomeBannerInput!Yes

CreateHomeBannerInput:

FieldTypeNotes
titleString!
descriptionString
actionsString!
urlString
isInternalLinkBoolean!
priorityFloat!
statusGeneralStatusTypeDRAFT or PUBLISHED
startDate / endDateDateTime!Required — both bound the banner’s active window
imageImageInput!Required — { filename: String!, filesize: Float!, mimeType: String!, url: String!, sequence: Float = 0 }
mobileImageImageInputOptional

Returns: HomeBanner!

Auth required: JWT, with the HOME_BANNER / UPDATE permission.

Arguments: input: UpdateOneHomeBannerInput!{ id: Float!, update: UpdateHomeBannerInput! }. Every field on UpdateHomeBannerInput is optional (same field set as CreateHomeBannerInput, minus the ! required markers) — send only what is changing.

Returns: HomeBanner!

Soft-deletes a banner.

Auth required: JWT, with the HOME_BANNER / DELETE permission.

Arguments: input: DeleteOneHomeBannerInput!{ id: Int! }.

Returns: HomeBannerDeleteResponse! — a mirror of HomeBanner’s scalar fields (all nullable), reflecting the just-deleted record’s last state.

mutation DeleteOneHomeBanner($input: DeleteOneHomeBannerInput!) {
deleteOneHomeBanner(input: $input) {
id
title
}
}

A per-tenant catalog of uploaded assets (images, documents, videos) — mainly used as the backing store for home banner images.

Fetches a single asset by id.

Auth required: JWT.

Arguments: id: Int (optional in the schema, but omitting it throws a BadRequestException — always pass it in practice).

Returns: MediaLibrary!

FieldTypeNotes
idInt!
filenameString!
filesizeFloat!
mimeTypeString!
typeMediaType!IMAGE, DOCUMENT, or VIDEO
pathString!Stored object path
urlStringPlayable/downloadable URL derived from path; null for unrecognized media types
thumbnailString
extraJSON
createdAt / updatedAtDateTime!

Lists media assets for the caller’s tenant.

Auth required: JWT.

Arguments:

ArgumentTypeDefault
filterMediaLibraryFilter!{}
pagingOffsetPaging!{ limit: 10 }
sorting[MediaLibrarySort!]![{ direction: DESC, field: createdAt }]

MediaLibraryFilter supports id, filename, type, createdAt, updatedAt.

Returns: MediaLibraryOffsetConnection!{ nodes: [MediaLibrary!]!, pageInfo: OffsetPageInfo!, totalCount: Int! }

Per-user, internal-source notifications (the NotificationSourceType.INTERNAL scope — this API does not surface externally-sourced notification records, if any exist).

Auth required: JWT. Always scoped to the calling user (userId is forced from the JWT, not client-suppliable via filter).

Arguments:

ArgumentTypeDefault
filterNotificationFilter!{}
pagingOffsetPaging!{ limit: 10 }
sorting[NotificationSort!]![{ direction: DESC, field: createdAt }]

Returns: NotificationOffsetConnection!{ nodes: [Notification!]!, pageInfo: OffsetPageInfo!, totalCount: Int! }

Notification fields:

FieldTypeNotes
idInt!
titleString!
messageString!
typeNotificationType!REMINDER, SUCCESS, or WARNING
hasReadBoolean!
metaJSONObject!Arbitrary structured payload, shape depends on type
userIdFloat!
createdAt / updatedAtDateTime!

Auth required: JWT.

Arguments: None.

Returns: Float! — count of unread, internal-source notifications for the caller.

query UnreadCount {
getUnreadNotificationsCount
}

Marks a single notification as read. Filters by both id and the caller’s userId — you cannot mark another user’s notification as read.

Auth required: JWT.

Arguments: id: Int (optional in the schema; pass a real id in practice).

Returns: Boolean!

Marks every internal-source notification belonging to the caller as read.

Auth required: JWT.

Arguments: None.

Returns: Boolean!

mutation MarkAllAsRead {
markAllAsRead
}

For the broader admin surface this page is part of, see Admin API Overview. For the full GraphQL operation index, see API Reference Overview.