Organizações e espaços de trabalho
Organizações e espaços de trabalho do Nodaro em TypeScript: crie escolas e equipes, convide membros, distribua códigos de entrada e veja o uso de créditos.
Disponível em Nodaro Cloud
client.organizations gerencia organizações, uma escola ou uma equipe, com os membros, os convites, o log de auditoria e os relatórios de uso delas. client.workspaces gerencia os espaços de trabalho dentro delas, como uma turma ou um projeto de equipe, com os membros e os códigos de entrada deles. Os métodos chamam a API REST de espaços de trabalho. Veja Espaços de trabalho para os conceitos.
As organizações são um recurso do Nodaro Cloud, ativado por instância. Em uma instância sem elas, todos os métodos desta página lançam NotFoundError.
Pertencer e atuar
Pertencer a um espaço de trabalho e atuar nele são coisas diferentes. Estes dois recursos controlam quem pertence a cada lugar. Para atuar em um espaço de trabalho, de modo que as listas venham dele e o trabalho novo fique nele, crie um cliente com client.withWorkspace(workspaceId) ou passe workspaceId para createClient:
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // runs in the class
await client.workflows.run(workflowId) // runs in your personal spaceclient.me() retorna as organizações e os espaços de trabalho a que você pertence, além de lastWorkspaceId.
O servidor decide todas as permissões. Ele responde se quem faz a chamada pode convidar, remover ou renomear, e a resposta chega como um erro. Não tente adivinhar isso no seu cliente, porque uma configuração pode mudar a resposta.
Métodos
client.organizations
organizations.list()
Lista as organizações a que você pertence (GET /v1/orgs).
list(): Promise<{ data: OrganizationView[] }>const { data: orgs } = await client.organizations.list()organizations.get(id)
Lê uma organização (GET /v1/orgs/:id).
get(id: string): Promise<{ data: OrganizationView }>Prop
Type
const { data: org } = await client.organizations.get(orgId)
console.log(org.status) // pending, active, suspended or deletedorganizations.create(input)
Cria uma organização, com você como proprietário (POST /v1/orgs). Uma nova organização normalmente começa como pending e passa a active quando o Nodaro a aprova. Enquanto ela está pendente, só você consegue vê-la e nada nela pode mudar, então avise o usuário de que ela aguarda aprovação.
create(input: { name: string; kind: "school" | "team"; slug?: string; acceptTerms?: boolean; settings?: OrgSettings }): Promise<{ data: OrganizationView }>Prop
Type
const { data: school } = await client.organizations.create({
name: "Sunrise School",
kind: "school",
acceptTerms: true,
})Uma escola sem acceptTerms: true falha com 400 terms_required.
organizations.update(id, input)
Renomeia uma organização ou altera as configurações dela (PATCH /v1/orgs/:id).
update(id: string, input: { name?: string; settings?: OrgSettings }): Promise<{ data: OrganizationView }>Prop
Type
await client.organizations.update(orgId, {
settings: { default_workflow_visibility: "workspace", allowed_email_domains: ["school.example"] },
})As configurações funcionam em camadas: uma configuração do espaço de trabalho prevalece sobre a da organização, que prevalece sobre o padrão do tipo dela. Definir uma chave como false é um valor real, e não um “não definido”.
| Chave | Tipo | Significado |
|---|---|---|
admin_access | view ou edit | O que os administradores podem fazer com o workflow de um membro |
default_workflow_visibility | private ou workspace | A visibilidade de um novo workflow |
member_access_to_shared | view ou edit | O que os membros podem fazer com um workflow compartilhado com o espaço de trabalho |
members_can_create_projects | booleano | Se os membros podem criar projetos no espaço de trabalho |
member_caps_enabled | booleano | Se valem limites de créditos por membro |
personal_space_enabled | booleano | Se os membros mantêm um espaço pessoal |
workspace_admins_can_invite | booleano | Se os administradores do espaço de trabalho podem convidar novas pessoas |
collaborators_can_invite | booleano | Se um colaborador com permissão de edição pode convidar mais colaboradores |
allowed_email_domains | lista de strings | Só na organização: os domínios de e-mail que podem entrar |
vocabulary_overrides | objeto | Só na organização: novos rótulos para as palavras do tipo, como { "workspace": "Cohort" }. Adicione "workspace_gender": "m" para uma palavra de espaço de trabalho gramaticalmente masculina, para que as frases em hebraico e em português do Brasil concordem com ela |
As duas chaves exclusivas da organização são substituídas por inteiro quando você as atualiza.
organizations.delete(id)
Exclui uma organização (DELETE /v1/orgs/:id). Ela fica oculta para os antigos membros, mas nada é destruído. Arquive todos os espaços de trabalho antes: caso contrário, a chamada falha com 409 has_active_workspaces.
delete(id: string): Promise<{ data: { id: string; status: string } }>Prop
Type
await client.organizations.delete(orgId)organizations.transferOwnership(id, userId)
Torna outro membro o proprietário (POST /v1/orgs/:id/transfer-ownership). Você passa a ser administrador.
transferOwnership(id: string, userId: string): Promise<{ data: { orgId: string; ownerUserId: string } }>Prop
Type
await client.organizations.transferOwnership(orgId, newOwnerId)organizations.leave(id)
Sai de uma organização (POST /v1/orgs/:id/leave). O proprietário não pode sair: transfira a propriedade antes, ou a chamada falha com 409 owner_cannot_leave.
leave(id: string): Promise<{ data: { orgId: string; left: boolean } }>Prop
Type
await client.organizations.leave(orgId)Membros da organização
Liste, altere e remova os membros de uma organização (/v1/orgs/:id/members). Um membro está active ou suspended; um membro suspenso mantém a vaga, mas não pode atuar. O proprietário não pode ser suspenso, removido nem rebaixado.
listMembers(orgId: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<OrgMemberView>>
updateMember(orgId: string, userId: string, input: { role?: "admin" | "member"; status?: "active" | "suspended" }): Promise<{ data: OrgMemberView }>
removeMember(orgId: string, userId: string): Promise<{ data: { removed: boolean } }>Prop
Type
const { data: members, nextCursor } = await client.organizations.listMembers(orgId)
await client.organizations.updateMember(orgId, userId, { role: "admin" })organizations.invite(orgId, input)
Convida pessoas por e-mail (POST /v1/orgs/:id/invitations). O método retorna uma linha por endereço.
invite(orgId: string, input: {
emails: string[]
orgRole?: "admin" | "member"
workspaceId?: string
workspaceRole?: "admin" | "member"
}): Promise<{ data: InvitationDelivery[] }>Prop
Type
const { data: rows } = await client.organizations.invite(orgId, {
emails: ["teacher@school.example"],
workspaceId,
workspaceRole: "admin",
})
for (const row of rows) {
if (row.status !== "sent") showInviteLink(row.email, row.link) // not emailed: share the link yourself
}Exiba o link. Uma linha cujo status não é sent traz um link no lugar: a instalação não tem um provedor de e-mail, ou a entrega falhou. O convite existe de qualquer forma, e sem o link ninguém consegue chegar até ele. Um convite expira depois de 14 dias. O limite diário de convites da organização responde com RateLimitedError.
Gerenciar convites
Liste, revogue e reenvie convites.
listInvitations(orgId: string, opts?: { status?: "open" | "accepted" | "revoked" | "expired"; workspaceId?: string; cursor?: string; limit?: number }): Promise<OrgPage<InvitationView>>
revokeInvitation(id: string): Promise<{ data: { id: string; revoked: boolean } }>
resendInvitation(id: string): Promise<{ data: InvitationDelivery & { id: string } }>Prop
Type
const { data: open } = await client.organizations.listInvitations(orgId, { status: "open" })
await client.organizations.resendInvitation(open[0].id)Ver e aceitar um convite
previewInvitation() é público: funciona enquanto a pessoa convidada ainda não entrou na conta e retorna o endereço mascarado. acceptInvitation() exige que quem faz a chamada tenha entrado na conta com o mesmo e-mail do convite.
previewInvitation(token: string): Promise<{ data: InvitationPreview }>
acceptInvitation(token: string): Promise<{ data: { orgId: string; workspaceId: string | null } }>Prop
Type
const { data: preview } = await client.organizations.previewInvitation(token)
const { data: joined } = await signedInClient.organizations.acceptInvitation(token)A aceitação falha com 400 invitation_expired, invitation_revoked, invitation_accepted ou email_mismatch, e com um ForbiddenError quando a organização só admite domínios de e-mail da lista.
organizations.audit(orgId, opts?)
Lê o log de auditoria da organização, do mais recente para o mais antigo (GET /v1/orgs/:id/audit). O log continua legível enquanto a organização está suspensa.
audit(orgId: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<OrgAuditEntry>>Prop
Type
const { data: entries } = await client.organizations.audit(orgId, { limit: 50 })
for (const e of entries) console.log(e.createdAt, e.action, e.actor?.displayName ?? "system")Cada entrada tem id, workspaceId, action, targetType, targetId, details, createdAt e actor, que é null nas ações que o sistema executou. action é uma lista aberta: mostre as ações que você conhece e a string bruta das demais. Um código que trata só uma lista fixa quebra na primeira ação nova.
Uso da organização
Relatórios de uso de créditos para um intervalo de datas, para o proprietário e os administradores da organização (GET /v1/orgs/:id/usage). usage() agrupa o relatório, usageRows() retorna as execuções por trás dele, das mais recentes para as mais antigas, uma página por vez, e usageCsv() retorna o relatório ou as linhas como texto CSV.
usage(orgId: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: "workspace" | "member" | "model" | "day"; workspaceId?: string; userId?: string }): Promise<{ data: UsageReport }>
usageRows(orgId: string, opts?: { from?: string; to?: string; tz?: string; workspaceId?: string; userId?: string; cursor?: string; limit?: number }): Promise<OrgPage<UsageLogEntry>>
usageCsv(orgId: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: string; workspaceId?: string; userId?: string }): Promise<string>Prop
Type
const { data: report } = await client.organizations.usage(orgId, {
from: "2026-09-01",
to: "2026-09-30",
tz: "Europe/Rome",
groupBy: "workspace",
})
const csv = await client.organizations.usageCsv(orgId, { from: "2026-09-01", to: "2026-09-30" })Cada linha informa três valores de créditos. credits é o quanto as execuções custaram até agora: o valor liquidado das execuções terminadas e a reserva retida das demais. settledCredits e inFlightCredits dividem esse valor em duas partes. Os totais cobrem o intervalo inteiro, mesmo quando um agrupamento está truncated.
platformAbsorbedCreditsé o excedente de uma execução cobrada por uso que passou do orçamento restante do espaço de trabalho, excedente que a plataforma absorve.chargedToBudgeté igual asettledCreditsmenosplatformAbsorbedCredits: os créditos liquidados que chegaram ao orçamento.appMarkupAbsorbedCreditsé o acréscimo de um app que o orçamento não conseguiu cobrir. Ele não tem uma execução no relatório, então não faz parte dechargedToBudget.
As exportações em CSV são limitadas a dez por minuto por usuário.
client.workspaces
workspaces.list()
Lista os espaços de trabalho a que você pertence e o seu lastWorkspaceId (GET /v1/workspaces). O método retorna resumos, a mesma lista que client.me() traz.
list(): Promise<{ data: WorkspaceSummary[]; lastWorkspaceId: string | null }>const { data: workspaces, lastWorkspaceId } = await client.workspaces.list()workspaces.listForOrg(orgId, opts?)
Lista os espaços de trabalho de uma organização (GET /v1/orgs/:id/workspaces).
listForOrg(orgId: string, opts?: { includeArchived?: boolean }): Promise<{ data: WorkspaceView[] }>Prop
Type
const { data: classes } = await client.workspaces.listForOrg(orgId)workspaces.get(id)
Lê um espaço de trabalho completo (GET /v1/workspaces/:id).
get(id: string): Promise<{ data: WorkspaceView }>Prop
Type
const { data: workspace } = await client.workspaces.get(workspaceId)workspaces.create(orgId, input)
Cria um espaço de trabalho em uma organização (POST /v1/orgs/:id/workspaces). A organização precisa estar ativa.
create(orgId: string, input: { name: string; slug?: string; description?: string; settings?: WorkspaceSettings }): Promise<{ data: WorkspaceView }>Prop
Type
const { data: classOne } = await client.workspaces.create(orgId, { name: "Class 1" })workspaces.update(id, input)
Altera um espaço de trabalho (PATCH /v1/workspaces/:id).
update(id: string, input: { name?: string; description?: string | null; settings?: WorkspaceSettings }): Promise<{ data: WorkspaceView }>Prop
Type
await client.workspaces.update(workspaceId, { settings: { members_can_create_projects: true } })workspaces.setArchived(id, archived)
Arquiva ou desarquiva um espaço de trabalho (POST /v1/workspaces/:id/archive ou /unarchive). Arquivar pode ser desfeito e não destrói nada: o espaço de trabalho deixa de aceitar trabalho novo e continua totalmente legível. Uma gravação em um espaço de trabalho arquivado falha com um ForbiddenError.
setArchived(id: string, archived: boolean): Promise<{ data: WorkspaceView }>Prop
Type
await client.workspaces.setArchived(workspaceId, true)Membros do espaço de trabalho
Liste, adicione, altere e remova os membros de um espaço de trabalho (/v1/workspaces/:id/members). Uma pessoa precisa já pertencer à organização para ser adicionada; para trazer alguém novo, convide a pessoa. Os proprietários e administradores da organização são administradores de todos os espaços de trabalho sem aparecer na lista.
listMembers(id: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<WorkspaceMemberView>>
addMember(id: string, input: { userId: string; role: "admin" | "member" }): Promise<{ data: WorkspaceMemberView }>
updateMember(id: string, userId: string, input: { role?: "admin" | "member"; status?: "active" | "suspended"; creditCap?: number | null }): Promise<{ data: WorkspaceMemberView }>
removeMember(id: string, userId: string): Promise<{ data: { removed: boolean } }>Prop
Type
await client.workspaces.addMember(workspaceId, { userId, role: "member" })
await client.workspaces.updateMember(workspaceId, userId, { creditCap: 500 })Adicionar alguém que não é um membro ativo da organização falha com 400 not_org_member, e adicionar alguém duas vezes falha com 409 already_a_member.
Códigos de entrada
Um código de entrada permite que as pessoas entrem em um espaço de trabalho sem convite. Só os administradores do espaço de trabalho podem ler ou alterar o código.
getJoinCode(id: string): Promise<{ data: JoinCodeView | null }>
actOnJoinCode(id: string, action: "rotate" | "enable" | "disable"): Promise<{ data: JoinCodeView }>Prop
Type
const { data: code } = await client.workspaces.actOnJoinCode(workspaceId, "enable")
console.log(code.code) // for example BCDFGHJKgetJoinCode() retorna null quando ainda não foi criado nenhum código.
workspaces.join(code)
Entra em um espaço de trabalho com o código de entrada dele (POST /v1/workspaces/join). Funciona seja qual for o espaço de trabalho em que o seu cliente atua, então uma seleção desatualizada nunca impede a entrada.
join(code: string): Promise<{ data: { orgId: string; workspaceId: string } }>Prop
Type
const { data: joined } = await client.workspaces.join("BCDFGHJK")Um código desconhecido ou desativado, ou o código de um espaço de trabalho arquivado, falha com 400 join_code_invalid: uma única resposta para os três casos, para que um código não revele o que existe. Tentativas demais respondem com RateLimitedError.
Uso do espaço de trabalho
Os mesmos relatórios de créditos da organização, para um espaço de trabalho (GET /v1/workspaces/:id/usage). Um membro vê as próprias execuções; um administrador vê as de todos e pode filtrar por userId.
usage(id: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: "member" | "model" | "day"; userId?: string }): Promise<{ data: UsageReport }>
usageRows(id: string, opts?: { from?: string; to?: string; tz?: string; userId?: string; cursor?: string; limit?: number }): Promise<OrgPage<UsageLogEntry>>
usageCsv(id: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: string; userId?: string }): Promise<string>Prop
Type
const { data: byModel } = await client.workspaces.usage(workspaceId, { groupBy: "model" })Erros
O servidor decide todas as permissões e responde com um erro. No SDK, um 403 vira ForbiddenError, um 404 vira NotFoundError e um 429 vira RateLimitedError; leia a message deles para saber o motivo. Os outros status chegam como NodaroError, com o code do servidor.
| Status | Código | Quando |
|---|---|---|
| 400 | validation_error | Um campo é inválido, um cursor está malformado, ou um relatório de uso tem data, fuso horário ou intervalo inválido |
| 400 | terms_required | Uma escola foi criada sem acceptTerms: true |
| 400 | not_org_member | A pessoa adicionada a um espaço de trabalho não é um membro ativo da organização dele |
| 400 | token_workspace_mismatch | Um token vinculado a um espaço de trabalho foi usado com outro espaço de trabalho |
| 400 | join_code_invalid | O código de entrada não existe, está desativado ou pertence a um espaço de trabalho arquivado |
| 400 | invitation_expired, invitation_revoked, invitation_accepted, email_mismatch | O convite não pode ser aceito |
| 403 | A função é baixa demais, o membro está suspenso, a organização não está ativa, o espaço de trabalho está arquivado, ou o domínio de e-mail não é permitido | |
| 404 | A organização, o espaço de trabalho, o membro ou o convite não existe, ou você não é membro dele | |
| 409 | name_taken | O slug está em uso |
| 409 | already_a_member | A pessoa já está no espaço de trabalho |
| 409 | owner_cannot_leave | O proprietário tentou sair |
| 409 | has_active_workspaces | A organização ainda tem espaços de trabalho que não estão arquivados |
| 429 | Organizações criadas, tentativas de entrada, exportações CSV ou convites em excesso | |
| 503 | billing_unavailable | Os relatórios de uso ainda não estão disponíveis nesta instância |
| 503 | audit_unavailable | Não foi possível registrar uma exportação CSV no log de auditoria. Tente de novo. |
Perguntas frequentes
Páginas relacionadas
Espaços de trabalho
Cliente
Espaços de trabalho e organizações
Workflows e projetos
Última atualização
Seletores, predefinições e prompts
Em TypeScript, leia as opções válidas dos seletores, preencha-os descrevendo a cena, carregue predefinições e melhore prompts com o Assistente de prompt.
OAuth e apps de desenvolvedor
Registre e gerencie apps OAuth do Nodaro com client.developerApps, e troque códigos, revogue tokens e leia dados da tela de consentimento com client.oauth.