Skip to main content

Assign user access

This guide assigns roles to a site user. To give organization members access to a site, use organization members instead.

Console-only site access​

Organization membership and site access for members are managed only in Console, on the organization's Members and Invitations screens. The role changes below apply to site users.

Assign and revoke roles​

Before you begin​

Confirm the username and role slug exist in the selected site. You must be signed in to change roles. Use Site roles to create or change a Site Role; this page only assigns or revokes existing roles.

Choose the assignment duration​

  • Permanent assignment: Omit expires_at. The membership has no expiry.
  • Expiring assignment: Supply a future ISO 8601 timestamp in expires_at. The same timestamp applies to every newly created username and role pair in that request.

The API rejects an expires_at value in the past. An active membership has no expires_at value or one later than the current time. After it expires, the membership record remains, but active role and app-access reads exclude it.

An existing username and role pair is silently skipped and counted as a success; the skip leaves its expiry unchanged. The JavaScript and Python user SDKs do not expose the dedicated expiry-update endpoint, so confirm the intended expiry before creating the assignment — assigning the same role again doesn't change it.

In TaruviBase Console, use the site's Users screen. In code, use the SDK calls below. These examples create one permanent assignment and one expiring assignment, then revoke the permanent assignment:

import {User} from '@taruvi/sdk';

const users = new User(taruvi);

const permanentAssignment = await users.assignRoles({
roles: ["support-viewer"],
usernames: ["onboarding-user"],
});

const expiringAssignment = await users.assignRoles({
roles: ["support-viewer"],
usernames: ["temporary-user"],
expires_at: "2027-01-31T23:59:59Z",
});

const revokeResult = await users.revokeRoles({
roles: ["support-viewer"],
usernames: ["onboarding-user"],
});
permanent_assignment = client.users.assign_roles(
roles=["support-viewer"], usernames=["onboarding-user"]
)

expiring_assignment = client.users.assign_roles(
roles=["support-viewer"],
usernames=["temporary-user"],
expires_at="2027-01-31T23:59:59Z",
)

client.users.revoke_roles(
roles=["support-viewer"], usernames=["onboarding-user"]
)

Replace the example expiry with a future ISO 8601 timestamp that matches the intended access window. To make an assignment permanent, leave out expires_at.

POST/api/assign/roles/
curl -X POST "$TARUVI_SITE_URL/api/assign/roles/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
{
"roles": [
"$TARUVI_ROLE_SLUG"
],
"usernames": [
"$TARUVI_USERNAME"
]
}
JSON

200

DELETE/api/revoke/roles/
curl -X DELETE "$TARUVI_SITE_URL/api/revoke/roles/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
{
"roles": [
"$TARUVI_ROLE_SLUG"
],
"usernames": [
"$TARUVI_USERNAME"
]
}
JSON

200

Assignment can return HTTP 200 with data.failures for individual username and role pairs. Revocation can also return HTTP 200 with data.failures. Successful pairs remain applied in either partial result. Re-read the user's roles and app access, record each failed username and role, correct the selected site, slug, username, or expiry, then retry only those failed pairs.

The JavaScript SDK declares RolesResponse data as {count: number}, but that type does not match either backend response. Full success contains status and message with no data; a partial result contains data.failures and no count. Treat the result as unknown and check its shape at runtime instead of trusting the declared data type:

type RoleFailure = {username: string; role: string; error: string};

function isRoleFailure(value: unknown): value is RoleFailure {
if (typeof value !== "object" || value === null) return false;
const candidate = value as Partial<RoleFailure>;
return (
typeof candidate.username === "string" &&
typeof candidate.role === "string" &&
typeof candidate.error === "string"
);
}

function roleFailures(response: unknown): RoleFailure[] {
if (typeof response !== "object" || response === null) return [];
const data = (response as {data?: unknown}).data;
if (typeof data !== "object" || data === null) return [];
const failures = (data as {failures?: unknown}).failures;
return Array.isArray(failures) ? failures.filter(isRoleFailure) : [];
}

Pass each assignment or revocation result to roleFailures as unknown. An empty failure list means success only when the response also has the success message. Don't read a count from the JavaScript SDK type.

For other problems, see troubleshooting.

Verify active access​

Re-read the user and available apps after an assignment:

import {User} from '@taruvi/sdk';

const users = new User(taruvi);
const userAfterAssignment = await users.getUser("temporary-user");
const appsAfterAssignment = await users.getUserApps("temporary-user");
user_after_assignment = client.users.get("temporary-user")
apps_after_assignment = client.users.apps("temporary-user")

Confirm the expected role appears in the user read and the expected app appears in the app read. After expires_at, repeat both reads and confirm the expired membership no longer contributes roles or app access. Refine providers 1.3.6 can read roles and apps; getMany and custom operations aren't supported.

GET/api/users/$TARUVI_USERNAME/apps/
curl "$TARUVI_SITE_URL/api/users/$TARUVI_USERNAME/apps/" \
-H "Authorization: Api-Key $TARUVI_API_KEY"

200

Assigning a role doesn't guarantee the user can do what you expect. Use Test access decisions to check an allowed case and a denied case for the intended resource and action.