Docs do Nodaro
DocumentaçãoReferência de nósModelosAgentes de IA (MCP)DesenvolvedoresSelf-hostingPesquisa
SDK para TypeScript

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 space

client.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

MétodoO que faz
organizations.list()Lista as organizações a que você pertence
organizations.get(id)Lê uma organização
organizations.create(input)Cria uma escola ou uma equipe
organizations.update(id, input)Renomeia a organização ou altera as configurações dela
organizations.delete(id)Exclui uma organização
organizations.transferOwnership(id, userId)Torna outro membro o proprietário
organizations.leave(id)Sai de uma organização
organizations.listMembers(), updateMember(), removeMember()Gerencia os membros
organizations.invite(orgId, input)Convida pessoas por e-mail
organizations.listInvitations(), revokeInvitation(), resendInvitation()Gerencia os convites enviados
organizations.previewInvitation(), acceptInvitation()Mostra e aceita um convite
organizations.audit(orgId, opts?)Lê o log de auditoria
organizations.usage(), usageRows(), usageCsv()Lê o uso de créditos
workspaces.list()Lista os espaços de trabalho a que você pertence
workspaces.listForOrg(orgId, opts?)Lista os espaços de trabalho de uma organização
workspaces.get(id)Lê um espaço de trabalho
workspaces.create(orgId, input)Cria um espaço de trabalho
workspaces.update(id, input)Altera um espaço de trabalho
workspaces.setArchived(id, archived)Arquiva ou desarquiva um espaço de trabalho
workspaces.listMembers(), addMember(), updateMember(), removeMember()Gerencia os membros do espaço de trabalho
workspaces.getJoinCode(), actOnJoinCode()Gerencia o código de entrada
workspaces.join(code)Entra em um espaço de trabalho com um código
workspaces.usage(), usageRows(), usageCsv()Lê o uso de créditos de um espaço de trabalho

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 deleted

organizations.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”.

ChaveTipoSignificado
admin_accessview ou editO que os administradores podem fazer com o workflow de um membro
default_workflow_visibilityprivate ou workspaceA visibilidade de um novo workflow
member_access_to_sharedview ou editO que os membros podem fazer com um workflow compartilhado com o espaço de trabalho
members_can_create_projectsbooleanoSe os membros podem criar projetos no espaço de trabalho
member_caps_enabledbooleanoSe valem limites de créditos por membro
personal_space_enabledbooleanoSe os membros mantêm um espaço pessoal
workspace_admins_can_invitebooleanoSe os administradores do espaço de trabalho podem convidar novas pessoas
collaborators_can_invitebooleanoSe um colaborador com permissão de edição pode convidar mais colaboradores
allowed_email_domainslista de stringsSó na organização: os domínios de e-mail que podem entrar
vocabulary_overridesobjetoSó 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 a settledCredits menos platformAbsorbedCredits: 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 de chargedToBudget.

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 BCDFGHJK

getJoinCode() 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.

StatusCódigoQuando
400validation_errorUm campo é inválido, um cursor está malformado, ou um relatório de uso tem data, fuso horário ou intervalo inválido
400terms_requiredUma escola foi criada sem acceptTerms: true
400not_org_memberA pessoa adicionada a um espaço de trabalho não é um membro ativo da organização dele
400token_workspace_mismatchUm token vinculado a um espaço de trabalho foi usado com outro espaço de trabalho
400join_code_invalidO código de entrada não existe, está desativado ou pertence a um espaço de trabalho arquivado
400invitation_expired, invitation_revoked, invitation_accepted, email_mismatchO convite não pode ser aceito
403A 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
404A organização, o espaço de trabalho, o membro ou o convite não existe, ou você não é membro dele
409name_takenO slug está em uso
409already_a_memberA pessoa já está no espaço de trabalho
409owner_cannot_leaveO proprietário tentou sair
409has_active_workspacesA organização ainda tem espaços de trabalho que não estão arquivados
429Organizações criadas, tentativas de entrada, exportações CSV ou convites em excesso
503billing_unavailableOs relatórios de uso ainda não estão disponíveis nesta instância
503audit_unavailableNão foi possível registrar uma exportação CSV no log de auditoria. Tente de novo.

Perguntas frequentes

Última atualização

Nesta página