Rioko

Versão 1 · atualizado a 18/09/2026

API de partners da Rioko

Tudo o que a equipa técnica de um partner precisa para ligar a sua plataforma à Rioko: convidar clientes, acompanhar o estado deles e manter as duas listas alinhadas.

Visão geral

A API serve para a plataforma de um partner convidar os seus clientes para a Rioko e saber, a cada momento, em que ponto está cada um: se já criou conta, se o partner já o confirmou, se deu acesso, que integrações tem ligadas e quando saiu o último documento.

É uma API de servidor para servidor. Não substitui o portal de partner: confirmar ou rejeitar um cliente que chegou pelo link, criar contas geridas, configurar integrações e pagar subscrições continuam a fazer-se no portal, por uma pessoa com sessão iniciada.

  • Não escreve nas integrações de nenhum cliente.
  • Não emite, finaliza nem anula documentos fiscais.
  • Não confirma nem rejeita clientes pendentes: uma chave comprometida não pode decidir quem são os clientes do partner.
  • Cada chave vê só os clientes do partner a que pertence.

Começar: chaves

As chaves criam-se no portal de partner, no separador API, pelo dono ou por um membro da equipa com permissão de administrador. Um membro só de leitura vê a lista mas não cria nem revoga.

A chave tem o formato rkp_live_ seguido de 40 caracteres hexadecimais e é mostrada uma única vez, no momento em que é criada. A Rioko guarda apenas um hash: se a chave se perder, revogue-a e crie outra.

  • Guarde a chave no servidor da sua plataforma (variável de ambiente ou cofre de segredos). Nunca no browser, numa app móvel ou num repositório.
  • Cada partner pode ter no máximo 2 chaves ativas. Para rodar uma chave sem interrupção: crie a nova, passe a plataforma para ela e só depois revogue a antiga.
  • Toda a chave expira. Por omissão ao fim de 365 dias, que é também o máximo.
  • Uma chave revogada deixa de funcionar na chamada seguinte.
  • Se a pessoa que criou a chave sair da equipa do partner na Rioko, as chaves que criou são revogadas automaticamente.

Fundamentos

TemaRegra
URL basehttps://rioko.online/api/partner/v1
FormatoJSON em UTF-8. Pedidos com corpo levam Content-Type: application/json.
DatasISO 8601 em UTC, por exemplo 2026-09-17T10:00:00.000Z.
VersõesDentro da v1 só entram mudanças compatíveis: campos novos nas respostas, endpoints novos. O código deve ignorar campos que não conhece. Uma mudança incompatível sai como v2, com aviso prévio.
Paginaçãolimit (1 a 100, por omissão 50) e cursor. Use o next_cursor da resposta anterior até vir null. Em /clients o cursor é opaco; em /invites é um deslocamento.
PollingNão há webhooks na v1. Consulte /clients com updated_since igual ao maior updated_at que já recebeu, no máximo de 15 em 15 minutos.
IdempotênciaNos convites, pelo external_ref: repetir o mesmo pedido não cria um segundo convite nem envia outro email.
Limites50 convites por pedido; 200 emails de convite por partner em 24 horas móveis, somando portal e API; no máximo 2 emails para o mesmo endereço em 24 horas. Pedidos em excesso por minuto são recusados com HTTP 429.
CORSNão há. A API não responde a pedidos do browser.

Autenticação

Todas as chamadas levam a chave no cabeçalho Authorization. Sem ele, o pedido é recusado antes de chegar à API.

Uma chave desconhecida, expirada ou revogada recebe exatamente a mesma resposta 401, de propósito. Um partner suspenso ou com os Termos de Partner por aceitar recebe 403.

curl https://rioko.online/api/partner/v1/me \
  -H "Authorization: Bearer rkp_live_3f9c…"
HTTPcodeQuando
401missing_keySem cabeçalho Authorization, ou sem o prefixo Bearer rkp_.
401invalid_keyChave com formato errado, desconhecida, expirada ou revogada.
403partner_inactiveO partner está suspenso, terminado, ou não aceitou a versão atual dos Termos de Partner.

Endpoints

GET/api/partner/v1/me

O partner da chave, os limites e a própria chave.

Útil para verificar a ligação e saber quantos convites ainda cabem nas últimas 24 horas.

Exemplo

curl https://rioko.online/api/partner/v1/me \
  -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200

{
  "partner": { "id": "exemplo", "name": "Exemplo Lda", "status": "active" },
  "landing_url": "https://rioko.online/pt/p/exemplo",
  "limits": {
    "invites_per_request": 50,
    "invites_per_24h": 200,
    "invites_left_24h": 187,
    "page_max": 100
  },
  "key": { "label": "Plataforma produção", "expires_at": "2027-09-17T10:00:00.000Z" }
}

Erros possíveis

missing_keyinvalid_keypartner_inactivemethod_not_allowed

POST/api/partner/v1/invites

Convida clientes por email.

Cada convite é um email com um link único (válido 30 dias e de uso único) para a página do partner na Rioko, onde o cliente cria conta ou entra, aceita as condições e escolhe se dá acesso ao partner.

Se o cliente aceitar a partir de um email verificado igual ao do convite, fica confirmado de imediato. Se aceitar com outro email, fica pendente e o partner confirma-o no portal.

Com send_email a false a Rioko não envia o email: a resposta traz o invite_url e é a sua plataforma que o entrega. O convite conta na mesma para os limites.

O invite_url só vem na resposta que cria o convite. Um pedido repetido com o mesmo external_ref e o mesmo email responde exists, sem link e sem novo email.

Um endereço que já é cliente de outro partner na Rioko responde sent como qualquer outro, mas não recebe email: nenhuma resposta da API revela quem já usa a Rioko.

Parâmetros

CampoTipoObrigatórioRegras
invitesarraysim1 a 50 elementos.
invites[].emailstringsimUm endereço de email. É guardado em minúsculas.
invites[].external_refstringnãoO id do cliente na sua plataforma, até 128 caracteres. Único por partner entre ligações que não terminaram.
send_emailbooleannãoPor omissão true.

Exemplo

curl -X POST https://rioko.online/api/partner/v1/invites \
  -H "Authorization: Bearer rkp_live_3f9c…" \
  -H "Content-Type: application/json" \
  -d @convites.json

Pedido

{
  "invites": [
    { "email": "[email protected]", "external_ref": "acad_1042" },
    { "email": "[email protected]", "external_ref": "acad_1043" }
  ],
  "send_email": true
}

Resposta · 201 se criou pelo menos um convite, 200 se não

{
  "results": [
    {
      "email": "[email protected]",
      "external_ref": "acad_1042",
      "status": "sent",
      "id": "6f1c2d0e-8a4b-4f3e-9b8e-2c1d5e7a9b10",
      "invite_url": "https://rioko.online/pt/p/exemplo/exemplo-3fa91c0e5b7d2a8f4c6e1b09",
      "expires_at": "2026-10-17T10:00:00.000Z"
    },
    {
      "email": "[email protected]",
      "external_ref": "acad_1043",
      "status": "exists",
      "id": "9b2e7a4c-1d3f-4e5a-8b6c-0d9e2f1a3b4c"
    }
  ],
  "invites_left_24h": 186
}

Erros possíveis

invalid_requesttoo_many_invitesmissing_keyinvalid_keypartner_inactive

GET/api/partner/v1/invites

Os convites enviados, do mais recente para o mais antigo.

Nunca devolve tokens nem links. status é open, expired, accepted ou revoked.

Parâmetros

ParâmetroTipoObrigatórioRegras
statusstringnãoopen, expired, accepted ou revoked.
limitinteironão1 a 100, por omissão 50.
cursorstringnãoO next_cursor da página anterior.

Exemplo

curl "https://rioko.online/api/partner/v1/invites?status=open&limit=50" \
  -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200

{
  "data": [
    {
      "id": "6f1c2d0e-8a4b-4f3e-9b8e-2c1d5e7a9b10",
      "external_ref": "acad_1042",
      "email": "[email protected]",
      "status": "open",
      "sent_at": "2026-09-17T10:00:00.000Z",
      "expires_at": "2026-10-17T10:00:00.000Z",
      "accepted_at": null,
      "resends": 0
    }
  ],
  "next_cursor": null
}

Erros possíveis

invalid_cursormissing_keyinvalid_keypartner_inactive

DELETE/api/partner/v1/invites/{id}

Revoga um convite aberto.

O link do email deixa de funcionar. Só revoga convites do próprio partner que ainda estejam abertos.

Exemplo

curl -X DELETE https://rioko.online/api/partner/v1/invites/6f1c2d0e-8a4b-4f3e-9b8e-2c1d5e7a9b10 \
  -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200

{ "id": "6f1c2d0e-8a4b-4f3e-9b8e-2c1d5e7a9b10", "status": "revoked" }

Erros possíveis

not_foundmissing_keyinvalid_keypartner_inactive

GET/api/partner/v1/clients

Os clientes do partner e o estado de cada um.

Ordenados por updated_at e depois por id, do mais antigo para o mais recente, o que torna o polling com updated_since estável.

updated_at muda com qualquer alteração à ligação: conta criada, confirmação ou rejeição, acesso dado ou retirado, entrega de uma conta gerida, fim da ligação.

O que cada cliente mostra depende do ponto em que está (ver Estados e consentimento): um cliente pendente mostra só nome da empresa, NIF, email e data; sem acesso, só nome, código e modo; com acesso, também as integrações e o último documento; uma ligação terminada, só o fim.

state, nos clientes com acesso, é live, connecting, signed_up, needs_attention ou subscription_inactive.

Em cada integração, subscription diz como está a subscrição Rioko do cliente para essa integração: active (paga), trial (em período experimental), inactive (terminou ou está suspensa: essa integração não fatura) ou none (nunca subscreveu). subscription_ending é true quando a subscrição não vai renovar.

Parâmetros

ParâmetroTipoObrigatórioRegras
updated_sincedata ISO 8601nãoSó clientes com updated_at estritamente posterior.
statusstringnãoactive ou ended.
validationstringnãopending ou confirmed.
limitinteironão1 a 100, por omissão 50.
cursorstringnãoO next_cursor da página anterior, tal como veio.

Exemplo

curl "https://rioko.online/api/partner/v1/clients?updated_since=2026-09-18T00:00:00.000Z" \
  -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200

{
  "data": [
    {
      "id": "0a7d…",
      "external_ref": "acad_1042",
      "updated_at": "2026-09-18T09:12:44.000Z",
      "status": "active",
      "validation": "pending",
      "company_name": "Academia Norte, Lda",
      "nif": "516000000",
      "email": "[email protected]",
      "claimed_at": "2026-09-18T09:12:44.000Z"
    },
    {
      "id": "4c1e…",
      "external_ref": "acad_0981",
      "updated_at": "2026-09-19T15:02:10.000Z",
      "status": "active",
      "validation": "confirmed",
      "client_code": "RIO-7K2M9Q",
      "name": "Estúdio Sul",
      "mode": "referred",
      "since": "2026-09-02T11:40:00.000Z",
      "access": false
    },
    {
      "id": "8e33…",
      "external_ref": "acad_0770",
      "updated_at": "2026-09-20T08:30:00.000Z",
      "status": "active",
      "validation": "confirmed",
      "client_code": "RIO-3H8TQ1",
      "name": "Clube Oeste",
      "mode": "referred",
      "since": "2026-08-21T10:00:00.000Z",
      "access": true,
      "state": "live",
      "connections": [
        { "source": "stripe", "destination": "moloni", "status": "active", "subscription": "active", "subscription_ending": false }
      ],
      "last_document_at": "2026-09-20T08:29:51.000Z"
    },
    {
      "id": "b51f…",
      "external_ref": "acad_0655",
      "updated_at": "2026-09-21T17:45:00.000Z",
      "status": "ended",
      "end_reason": "client_left",
      "ended_at": "2026-09-21T17:45:00.000Z"
    }
  ],
  "next_cursor": "MjAyNi0wOS0yMVQxNzo0NTowMC4wMDBafGI1MWY"
}

Erros possíveis

invalid_cursormissing_keyinvalid_keypartner_inactive

GET/api/partner/v1/clients/{client_code}

Um cliente, pelo código Rioko (RIO-…).

Só clientes confirmados têm código visível. Um código de outro partner responde 404, igual a um que não existe.

Exemplo

curl https://rioko.online/api/partner/v1/clients/RIO-3H8TQ1 \
  -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200: o mesmo objeto de /clients

{
  "id": "8e33…",
  "client_code": "RIO-3H8TQ1",
  "access": true,
  "state": "live"
}

Erros possíveis

not_foundmissing_keyinvalid_keypartner_inactive

GET/api/partner/v1/clients/{client_code}/documents

O que aconteceu aos documentos de um cliente, depois de emitidos.

Para a plataforma mostrar ao comerciante o que mudou sem ter de perguntar ao programa de facturação.

NÃO traz o número do documento nem o link: esses vivem no programa de facturação, e já foram devolvidos na resposta ao pedido que emitiu cada documento. Aqui está o que mudou DESDE então: um rascunho fechado à mão, um recibo, uma nota de crédito.

`state` é o ponto mais avançado a que o documento chegou: `issued`, `held`, `finalized`, `settled` ou `credited`.

`updated_since` devolve só o que mexeu desde essa data, que é como se sonda sem repetir tudo. O histórico de acontecimentos é limpo aos 90 dias (365 para os que são prova), por isso um documento antigo aparece com menos história.

Precisa de acesso concedido pelo cliente. Sem ele, 404, igual a um código que não existe.

Exemplo

curl "https://rioko.online/api/partner/v1/clients/RIO-3H8TQ1/documents?updated_since=2026-09-01T00:00:00Z&limit=50"   -H "Authorization: Bearer rkp_live_3f9c…"

Resposta · 200: uma página de documentos, do mais antigo ao mais recente

{
  "client_code": "RIO-3H8TQ1",
  "external_ref": "acad_42",
  "data": [
    {
      "external_id": "mf_acad42_1042",
      "document_id": "1025042934",
      "state": "settled",
      "held": null,
      "issued_at": "2026-09-24T09:12:00Z",
      "finalized_at": "2026-09-24T09:12:03Z",
      "settled_at": "2026-09-30T10:04:00Z",
      "credited_at": null,
      "updated_at": "2026-09-30T10:04:00Z"
    }
  ],
  "next_cursor": null
}

Erros possíveis

not_foundinvalid_cursormissing_keyinvalid_keypartner_inactive

Estados e consentimento

Um convite ou o link público do partner dão ao cliente o preço de partner e a atribuição ao partner. Nunca dão acesso aos dados do cliente: o acesso é uma escolha explícita do próprio cliente, que a pode retirar a qualquer momento.

  • end_reason: client_left (o cliente saiu), partner_dropped (o partner largou o cliente), rejected (o partner não reconheceu o cliente), account_deleted, admin.
  • Um external_ref de uma ligação que o partner rejeitou ou largou não pode ser reutilizado para convidar o mesmo cliente: o convite responde external_ref_closed.
Situaçãostatus / validationO que a API mostra
Convite enviado, ainda sem contaem /invites: openEmail, datas, número de reenvios.
Convite expirado ou revogadoem /invites: expired ou revokedO mesmo.
Cliente criou conta, falta a confirmação do partneractive / pendingNome da empresa, NIF, email, data.
Confirmado, sem acessoactive / confirmed, access falseNome, código RIO-, modo, desde quando.
Confirmado, com acessoactive / confirmed, access trueTambém state, integrações e último documento.
Ligação terminadaendedSó end_reason e ended_at.

Resultado de cada convite

statusSignificado
sentConvite criado e email enviado (ou entregue pela sua plataforma, com send_email false).
invalidNão é um endereço de email.
opted_outEste endereço pediu para não receber convites deste partner.
already_invitedJá há um convite aberto deste partner para este endereço.
too_soonEste endereço já recebeu 2 emails deste partner nas últimas 24 horas.
daily_limitO partner atingiu 200 emails de convite nas últimas 24 horas.
failedO fornecedor de email recusou a mensagem. Nada ficou guardado: pode tentar de novo.
unconfirmedNão houve confirmação do envio. O convite existe; se não chegar, reenvie pelo portal.
existsO mesmo external_ref e o mesmo email já têm convite ou cliente. Nada foi criado nem enviado.
external_ref_conflictEsse external_ref já pertence a outro endereço neste partner.
external_ref_closedEsse external_ref é de uma ligação que o partner rejeitou ou largou.

Referência de erros

Todos os erros têm a mesma forma. Trate pelo code, não pela mensagem.

{ "error": { "code": "invalid_key", "message": "The key is unknown, expired or revoked." } }
HTTPcodeSignificado
400invalid_requestCorpo ausente, JSON inválido ou campos em falta.
400too_many_invitesMais de 50 convites num pedido.
400invalid_cursorO cursor não é válido.
401missing_keySem chave.
401invalid_keyChave desconhecida, expirada ou revogada.
403partner_inactivePartner suspenso, terminado ou com os Termos por aceitar.
404not_foundO recurso não existe ou não é deste partner.
405method_not_allowedMétodo não suportado neste caminho.
429(sem corpo garantido)Demasiados pedidos por minuto. Espere e repita.
503unavailableIndisponível por momentos. Repita com espera crescente.

Dados que a API nunca devolve

O partner é responsável pelo tratamento dos dados que recebe, nos termos do contrato de partner e do acordo de tratamento de dados que assinou com a Kapta.

  • Credenciais de integrações (chaves de API, tokens OAuth, segredos de webhook).
  • Dados dos compradores dos clientes e o conteúdo das faturas.
  • Números de documentos fiscais.
  • Ids do Stripe (clientes, subscrições, pagamentos).
  • Emails ou nomes da equipa do partner.
  • Tokens de convite, exceto o invite_url na resposta que cria o convite.

Lista de segurança

  • A chave vive só no servidor, fora do código-fonte e dos logs.
  • Use uma chave por ambiente (produção e testes).
  • Rode as chaves antes de expirarem, usando a segunda chave para não haver interrupção.
  • Revogue de imediato uma chave que possa ter sido exposta.
  • Não registe invite_url em logs partilhados: quem tem o link pode aceitar o convite.
  • Faça polling com updated_since e respeite os 15 minutos; trate 429 e 503 com espera crescente.

Contrato de dados Stripe

Para as academias que recebem pagamentos no Stripe, a fatura só sai certa se o pagamento trouxer os dados de quem paga. A Kapta envia o documento completo; em resumo:

  • Identidade de quem paga no Customer: name, email, address e tax_ids (eu_vat, PT seguido do NIF). As renovações de subscrição leem o Customer.
  • Metadados repetidos onde o Stripe não os copia: metadata da Checkout Session, payment_intent_data.metadata e subscription_data.metadata. O NIF vai na chave nif.
  • Nenhuma outra chave pode ter nif, vat, tax, tin, iva, ust, cif ou fiscal no nome, e nenhum valor ou descrição pode ter 9 ou mais dígitos seguidos, exceto o NIF.
  • payment_intent_data.description estável para o mesmo produto (sem nome do aluno nem mês); Products com nomes estáveis nas subscrições.
  • Pagamentos na conta Stripe da academia (direct charges). A Rioko nunca pede nem aceita a chave da plataforma.

Ambiente de testes

Peça à Kapta um partner de testes (por exemplo exemplo-teste) e crie as chaves de teste no portal desse partner. Os convites e clientes de teste são registos reais na Rioko: use endereços de email que controla e avise a Kapta quando terminar, para o partner de testes ser encerrado.

GET /me, GET /invites e GET /clients são seguros a qualquer momento. POST /invites com send_email false não envia emails, mas conta para os limites.

Histórico de versões

DataVersãoAlterações
17/09/2026v1Primeira versão: /me, /invites, /clients.

Suporte

Dúvidas técnicas, chaves comprometidas ou um partner de testes: [email protected]. Indique o id do partner e, se houver, o id do convite ou do cliente. Nunca envie a chave.