Quando usar
Com o login único, o membro é criado no primeiro acesso — então você pode viver sem estes endpoints no começo. Eles servem para manter a Astorya alinhada com o seu sistema:
| Evento no seu sistema | Chamada |
|---|---|
| Membro contratou o plano com o benefício | PUT /users/{externalUserId} (opcional; a primeira sessão também cria) |
| Membro trocou nome ou e-mail | PUT /users/{externalUserId} com os novos dados |
| Pagamento em atraso, suspensão temporária | PUT com status: "BLOCKED" |
| Regularizou o pagamento | PUT com status: "ACTIVE" |
| Cancelou o plano | DELETE /users/{externalUserId} |
Em todas as rotas, {externalUserId} vai no caminho codificado para URL (encodeURIComponent), já que pode ter caracteres como @ ou /.
Criar ou atualizar
/partner/v1/users/{externalUserId}Cria o membro se ele ainda não existir no seu tenant ou atualiza os campos enviados. Campos omitidos não mudam. A chamada é idempotente: repetir com o mesmo corpo dá o mesmo resultado.
| Campo | Descrição |
|---|---|
emailstring | Novo e-mail. Se pertencer a outro membro: 409 EMAIL_CONFLICT. |
namestring | Nome exibido. |
status"ACTIVE" | "BLOCKED" | BLOCKED impede novas sessões (403 USER_REVOKED); ACTIVE libera de novo, inclusive depois de um DELETE. |
curl -X PUT "https://api.astorya.com.br/partner/v1/users/usuario-10293" \
-u "$ASTORYA_CLIENT_ID:$ASTORYA_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{ "name": "Maria Souza Lima", "email": "maria.lima@exemplo.com.br" }'export async function upsertMember(member) {
const res = await fetch(
`https://api.astorya.com.br/partner/v1/users/${encodeURIComponent(member.id)}`,
{
method: 'PUT',
headers: {
Authorization: `Basic ${basicAuth}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: member.name, email: member.email, status: 'ACTIVE' }),
},
);
if (!res.ok) throw new Error(`Astorya respondeu ${res.status}`);
return res.json();
}from urllib.parse import quote
def upsert_member(member: dict) -> dict:
response = requests.put(
f"{ASTORYA_API}/partner/v1/users/{quote(str(member['id']), safe='')}",
auth=AUTH,
json={"name": member["name"], "email": member["email"], "status": "ACTIVE"},
timeout=10,
)
response.raise_for_status()
return response.json()Nos exemplos em Node e Python, basicAuth, ASTORYA_API e AUTH são os mesmos do guia rápido (AUTH = (client_id, client_secret)).
Consultar
/partner/v1/users/{externalUserId}Retorna o membro e a data da última atividade (ouvir ou ler). Útil para a tela de suporte do seu time. Um externalUserId que nunca foi usado no seu tenant devolve 404.
curl "https://api.astorya.com.br/partner/v1/users/usuario-10293" \
-u "$ASTORYA_CLIENT_ID:$ASTORYA_CLIENT_SECRET"{
"id": "6702f1c9a8b4e2d3c1f0a9b8",
"externalUserId": "usuario-10293",
"email": "maria.lima@exemplo.com.br",
"name": "Maria Souza Lima",
"status": "ACTIVE",
"lastActivityAt": "2026-10-07T21:12:44.000Z"
}lastActivityAt é null se o membro ainda não ouviu nem leu nada.
Revogar o acesso
/partner/v1/users/{externalUserId}Revoga o acesso imediatamente e responde 204 No Content:
- As sessões ativas são encerradas. Se o membro estiver com a Astorya aberta, a WebView recebe
astorya:session-expirede, quando o seu app pedir uma nova sessão, a API responde 403USER_REVOKED— mostre algo como “O benefício de livros não está ativo no seu plano”. - O membro continua aparecendo nos relatórios dos meses em que teve uso.
- Se ele voltar a contratar, reative com
PUTestatus: "ACTIVE". A conta, a biblioteca e o progresso continuam lá.
Recomendação: chame o DELETE no mesmo ponto do seu sistema que processa o cancelamento do plano do usuário (ou o fim do período pago). Assim ninguém continua com acesso — e consumo nos relatórios — depois de sair.
curl -X DELETE "https://api.astorya.com.br/partner/v1/users/usuario-10293" \
-u "$ASTORYA_CLIENT_ID:$ASTORYA_CLIENT_SECRET"// Chame no fluxo de cancelamento do plano.
export async function revokeMember(externalUserId) {
const res = await fetch(
`https://api.astorya.com.br/partner/v1/users/${encodeURIComponent(externalUserId)}`,
{ method: 'DELETE', headers: { Authorization: `Basic ${basicAuth}` } },
);
// 404: o membro nunca acessou a Astorya — nada a revogar.
if (res.status !== 204 && res.status !== 404) {
throw new Error(`Astorya respondeu ${res.status}`);
}
}def revoke_member(external_user_id: str) -> None:
"""Chame no fluxo de cancelamento do plano."""
response = requests.delete(
f"{ASTORYA_API}/partner/v1/users/{quote(external_user_id, safe='')}",
auth=AUTH,
timeout=10,
)
# 404: o membro nunca acessou a Astorya, nada a revogar.
if response.status_code not in (204, 404):
response.raise_for_status()Se a chamada falhar (rede, 429, 5xx), coloque-a numa fila e tente de novo: a revogação é idempotente.