Documentação

Integração de parceiros com o Ivolution Analytics

Os parceiros da Ivolution podem ler os atletas, as fichas e os resultados das instituições que os autorizarem, e exibir os painéis delas dentro do próprio produto.

Antes de começar

Esta API é somente leitura. Ela expõe os dados que uma instituição produziu na Ivolution e autorizou a compartilhar.

Todas as rotas ficam sob /api/v1/partner/. A instituição não vai na rota: ela é indicada com o parâmetro de consulta institution_id em cada chamada de dados. Todas as respostas, de sucesso ou de erro, chegam dentro do mesmo envelope: status, data, message, errors e meta.

A referência completa, gerada a partir da especificação OpenAPI, está em /api/v1/partner/docs e permite testar cada operação pelo navegador.

Fora do escopo:

escritawebhookssincronização incrementalOAuth self-servicedados entre instituiçõessinal brutopainéis de grupoSDKs

O modelo em quatro palavras

Todo o sistema se explica com quatro termos. Vale a pena lê-los antes de qualquer endpoint.

TermoO que é
ParceiroA organização com acordo comercial vigente. É uma entidade do sistema, não uma pessoa nem uma instituição.
CredencialO que o parceiro apresenta para se autenticar. Ela é do parceiro, não de uma integração: a mesma credencial serve para todas. Indica quem está chamando.
IntegraçãoO vínculo que uma instituição autoriza com um parceiro. Define o que pode ser lido e é a unidade sobre a qual os acessos são auditados. Sem uma integração vigente, não há dados.
Código de vinculaçãoO código de uso único que a instituição obtém ao autorizar e entrega ao parceiro. É resgatado uma única vez e expira em 48 h. Não é uma credencial e não dá acesso por si só.

Uma credencial válida não basta para ler nada. Autenticação e escopo são coisas diferentes: a credencial identifica o parceiro, a integração determina quais instituições o autorizaram.

Autenticação

Cada chamada leva a credencial de parceiro no header Authorization: Bearer. A credencial tem o formato <prefixo>.<segredo>, e o prefixo indica o ambiente: uma credencial de produção começa com ivo_live_ e é rejeitada em qualquer outro ambiente, assim como a de teste em produção.

Para rotacioná-la, a Ivolution emite uma credencial nova e as duas funcionam ao mesmo tempo até que o sistema do parceiro termine a troca; só então a anterior é revogada, sem interromper o serviço.

A credencial também define os escopos: athletes para atletas e fichas, results para resultados e dashboards para o catálogo e as URLs dos painéis. Se faltar o que a rota exige, a resposta é 403 insufficient_scope.

GET/api/v1/partner/institutions
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/institutions" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Accept: application/json"
200Instituições com integração ativa
{
  "status": "success",
  "data": [
    {
      "institution_id": 42,
      "name": "Club Deportivo Ejemplo",
      "authorized_at": "2026-03-12T14:02:00Z"
    }
  ],
  "message": null,
  "errors": null,
  "meta": {
    "pagination": null,
    "cursor": { "next_cursor": null, "has_more": false }
  }
}

A lista mostra apenas as instituições com integração ativa. Ela não depende da assinatura: uma instituição listada pode responder subscription_required na próxima chamada de dados.

Vinculação passo a passo

A vinculação transforma um acordo comercial em permissão técnica sobre uma instituição específica. São quatro passos, e apenas dois cabem ao parceiro.

1
A instituição

Autoriza pelo Analytics

Um administrador da instituição acessa Configurações → Integrações e autoriza o parceiro. O sistema então gera um código de vinculação e o exibe uma única vez.

2
A instituição

Entrega o código ao parceiro

Pelo canal que as duas partes combinarem. O código expira em 48 h se não for resgatado e não dá acesso a nada sem a credencial do parceiro.

3
O parceiro

Resgata o código

Uma única chamada a POST /api/v1/partner/link, com a credencial de parceiro. O código é consumido no resgate: uma segunda tentativa responde 409 link_code_invalid.

4
O parceiro

Fica habilitado para ler

A integração passa a active, a resposta traz o institution_id e a instituição aparece na lista. Só então os recursos de dados respondem.

POST/api/v1/partner/link
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/link" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "EjemploDeCodigoDeVinculacion-NoEsReal_00000"}'
200Integração ativa
{
  "status": "success",
  "data": {
    "institution_id": 42,
    "name": "Club Deportivo Ejemplo",
    "authorized_at": "2026-03-12T14:02:00Z"
  },
  "message": null,
  "errors": null,
  "meta": null
}

O código é uma cadeia de 43 caracteres. Qualquer falha no resgate —um código desconhecido, emitido para outro parceiro, já resgatado ou expirado— responde o mesmo 409 link_code_invalid, sem distinguir qual.

O resgate é irreversível. Guarde o institution_id que ele devolve: é o parâmetro de consulta de todas as chamadas de dados. Se o código se perder antes do resgate, a instituição deve revogar a integração e autorizar novamente.

Estados de uma integração

Apenas um dos quatro permite a leitura. Os outros três devolvem erro, e o sistema do parceiro deve distingui-los para exibir uma mensagem útil.

EstadoO que significaLê?
pendingA instituição autorizou, mas o código ainda não foi resgatado.Não: 409 integration_pending
activeResgatado. É o único estado que dá acesso.Sim
expiredO código expirou sem ser resgatado. Terminal.Não: 409 integration_expired
revokedA instituição cortou o acesso. Terminal.Não: 403 integration_revoked

expired e revoked são terminais, mas não bloqueiam uma nova autorização: a instituição pode autorizar novamente quando quiser, e isso gera outro código. Sem nenhuma integração com essa instituição, a resposta é 404 integration_not_found.

Para verificar uma integração sem ler dados, existe GET /api/v1/partner/integration, com o mesmo institution_id: ele responde 200 apenas se a integração estiver ativa e a instituição tiver uma assinatura ou um período de teste vigente.

Atletas e correspondência por e-mail

A lista devolve os atletas da instituição, ativos ou não, ordenados por id. Cada atleta traz id, active, name, last_name, email, sex e birthdate; os três últimos podem chegar como null.

GET/api/v1/partner/athletes?institution_id=42
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"
200Uma página de atletas
{
  "status": "success",
  "data": [
    {
      "id": 4821,
      "active": true,
      "email": "lucia.ejemplo@example.com",
      "name": "Lucía",
      "last_name": "Ejemplo",
      "sex": {
        "id": 2,
        "names": { "es": "Femenino", "en": "Female", "pt": "Feminino" }
      },
      "birthdate": "2004-05-14"
    }
  ],
  "message": null,
  "errors": null,
  "meta": {
    "pagination": null,
    "cursor": { "next_cursor": "eyJpZCI6NDJ9", "has_more": true }
  }
}

GET /api/v1/partner/athletes/{athlete_id} devolve os mesmos campos mais current_sheet: a ficha mais recente, ou null se o atleta não tiver nenhuma. GET /api/v1/partner/athletes/{athlete_id}/sheets devolve todas as fichas dele, da mais antiga à mais recente e sem paginação, para ver como o peso ou a posição mudaram ao longo do tempo.

Cada ficha traz id, sheet_date, height (cm), weight (kg) e, como itens de catálogo, sport, position, athlete_type, physical_condition, dominant_arm e dominant_leg. Um item de catálogo é um id com seus names em es, en e pt, ou null se não houver dado. Uma primeira ficha pode chegar com sheet_date em 2025-01-01: é a data fixa atribuída no cadastro, não um erro.

Só ficam visíveis os atletas da instituição indicada em institution_id: um athlete_id de outra instituição, um excluído ou um inexistente respondem igual, 404 athlete_not_found.

Correspondência com os atletas do parceiro

O cruzamento com os atletas do sistema do parceiro é feito por e-mail. A lista não filtra por e-mail: para isso existe a busca, que recebe o e-mail no corpo e devolve uma página com o mesmo formato da lista.

POST/api/v1/partner/athletes/search?institution_id=42
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/search?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "lucia.ejemplo@example.com"}'

A correspondência não diferencia maiúsculas nem acentos, e os espaços no início e no fim do e-mail buscado são removidos. Sem correspondências, a resposta é 200 com uma página vazia, não um erro; um e-mail vazio responde 422.

O e-mail vai sempre no corpo, nunca na URL. Assim ele não fica em um histórico do navegador, em um log de acesso nem em uma URL compartilhada.

Observações sobre a correspondência

  • Um e-mail pode devolver mais de um atleta. O e-mail não é único dentro de uma instituição: vários atletas podem compartilhá-lo. Nesse caso, desempate com sex e birthdate antes de guardar o id.
  • Um 404 sobre um `id` já salvo (athlete_not_found) significa que esse atleta deixou de estar disponível na instituição: refaça a correspondência por e-mail, em vez de tentar novamente o mesmo id.
  • Nos catálogos, a chave é o `id`. O esporte, a posição e os demais itens de catálogo são cruzados com os resultados do atleta pelo id, não pelo nome traduzido: o id de sport corresponde a sport_id nos resultados, e o de position, a position_id.

Resultados

Os resultados seguem o mesmo contrato da exportação da plataforma: cada linha é uma tentativa de um exercício, com uma coluna plana por campo e os mesmos nomes da exportação, como heightJump, maxForce ou exerciseDate, mais exercise_key e exercise_side.

Ficam de fora os dados pessoais da exportação —e-mail, documento, data de nascimento, idade e país—, os identificadores internos e o comentário livre do exercício. Só são devolvidos dados do Ivolution Trainer, seja qual for a fonte escolhida pela instituição: não é um parâmetro que possa ser solicitado.

GET/api/v1/partner/athletes/{athlete_id}/results?institution_id=42
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/4821/results?institution_id=42&from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"
200Uma linha, resumida: cada protocolo traz suas colunas
{
  "status": "success",
  "data": [
    {
      "institution": "Club Deportivo Ejemplo",
      "nameAthlete": "Lucía Ejemplo",
      "test": "CMJ",
      "exercise_key": "cmj",
      "exercise_side": null,
      "exerciseTypeName": "Countermovement Jump",
      "exerciseDate": "2026-09-03T14:20:00Z",
      "repetitionNumber": 1,
      "heightJump": 32.5,
      "maxForce": 1450.3
    }
  ],
  "message": null,
  "errors": null,
  "meta": {
    "pagination": null,
    "cursor": { "next_cursor": "eyJpZCI6NDJ9", "has_more": true }
  }
}

Filtros opcionais: evaluation_type, o protocolo (por exemplo balance_jumps_with_cea; um desconhecido responde 422); test, o exercise_key exato do exercício; e from e to, datas inclusivas segundo o dia do calendário da Argentina. Um atleta inativo devolve uma lista vazia.

As colunas variam conforme o protocolo. Qualquer campo pode chegar como null: uma linha só traz preenchidas as colunas que o seu protocolo produz. Leia-as pelo nome e tolere null, sem presumir um conjunto fixo.

Observações de uso

  • Só saem dados do Ivolution Trainer. Uma instituição que mede apenas com o Valkyria Trainer mostra seus atletas e suas fichas, mas não tem resultados, e seus painéis não mostram dados.
  • Os nomes de campo são os da exportação, com duas equivalências com o recurso de atleta: idAthlete é o id do atleta, e gender é o seu sex, como texto; o id desse dado chega em gender_id.
  • Para filtrar e agrupar, use chaves estáveis: o protocolo (evaluation_type) e exercise_key + exercise_side. Os rótulos em espanhol, como test ou Tipo_Evaluacion, podem mudar, e o nome do exercício inclui o lado.
  • `from` e `to` cortam pelo dia argentino (America/Argentina/Buenos_Aires), e exerciseDate chega em UTC, com Z. Uma avaliação feita em 18 de junho às 21:22 na Argentina chega como 2026-06-19T00:22:00Z e entra com to=2026-06-18.
  • Uma avaliação excluída deixa de vir, sem aviso. Nesta versão não há lápides nem uma lista do que foi excluído: para que ela deixe de aparecer, atualize o que já foi armazenado lendo novamente esse intervalo de datas.
  • Cada consulta leva vários segundos. Filtrar por evaluation_type a torna bem mais rápida, porque sem esse filtro todos os protocolos são consultados. É melhor armazenar o que já foi lido em vez de pedi-lo toda vez que uma ficha é aberta.

Paginação

A lista e a busca de atletas, os resultados e a lista de instituições são paginados por cursor. A página seguinte é pedida passando meta.cursor.next_cursor no parâmetro cursor, com os mesmos filtros; quando has_more chega como false, a coleção terminou.

limit vale 10 por padrão e no máximo 100: um valor maior é reduzido. As fichas e o catálogo de painéis não são paginados.

GET/api/v1/partner/athletes?institution_id=42&cursor=eyJpZCI6NDJ9&limit=100
# O cursor é opaco: não deve ser montado à mão nem reutilizado com outros filtros.
# Guarde o valor exatamente como chegou e devolva-o igual.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes?institution_id=42&cursor=eyJpZCI6NDJ9&limit=100" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"

Um cursor inválido, expirado ou usado com outros filtros responde 400 invalid_cursor. Nesse caso, peça a primeira página novamente.

Catálogo de painéis

Além dos dados brutos, os painéis do Analytics podem ser exibidos dentro do produto do parceiro. O catálogo devolve, completo e sem paginação, os painéis com escopo de atleta prontos para incorporar.

GET/api/v1/partner/dashboards?institution_id=42
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/dashboards?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"
200Catálogo de painéis
{
  "status": "success",
  "data": [
    {
      "id": 7,
      "names": { "es": "Resumen físico", "en": "Physical summary", "pt": "Resumo físico" },
      "analysis_type": null,
      "equipment": "platform"
    }
  ],
  "message": null,
  "errors": null,
  "meta": null
}

names traz o nome nos três idiomas, e analysis_type é um item de catálogo ou null.

Os `id` de painel são de cada ambiente. Obtenha-os do catálogo no ambiente de teste e em produção; não os fixe no código.

Observações de uso

  • Só há painéis por atleta, com os mesmos dados do Ivolution Trainer que os resultados: a URL assinada fixa a fonte no Ivolution Trainer, assim como a instituição e o atleta.
  • O equipamento chega como chave estável, não como nome traduzido: platform, dynamometer, encoder, inertial ou photo_cell, ou null se o painel não tiver equipamento associado.
  • Se o serviço de painéis não responder, a resposta é 503 dashboards_unavailable, sempre com Retry-After: aguarde esse tempo e tente novamente. O catálogo nunca chega parcial, e um catálogo vazio não deve ser armazenado no lugar dele.

O fluxo do iframe

Um painel não é incorporado com uma URL fixa. Solicita-se uma URL assinada para um atleta específico, ela é colocada em um iframe, e essa URL expira em dez minutos.

1

Solicitação da URL

Com o id do painel e o do atleta. O servidor verifica se o atleta pertence à instituição (se não, 404 athlete_not_found), se está ativo (se não, 409 athlete_inactive) e se o painel está no catálogo (se não, 404 dashboard_not_available).

2

Renderização no iframe

A URL é usada como está. Instituição, atleta e fonte vêm travados na assinatura: nenhum filtro do painel pode ser alterado pelo cliente.

3

Renovação ao expirar

Não há atualização automática. Se a visualização ficar aberta por mais de dez minutos, solicite outra URL e substitua o src.

POST/api/v1/partner/athletes/{athlete_id}/dashboards/{dashboard_id}/embed-url?institution_id=42
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/4821/dashboards/7/embed-url?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"locale": "pt"}'
200URL assinada, válida por 10 minutos
{
  "status": "success",
  "data": {
    "url": "<URL assinada: coloque-a como está no src do iframe>",
    "expires_at": "2026-09-03T14:30:00Z"
  },
  "message": null,
  "errors": null,
  "meta": null
}

O corpo é opcional: locale aceita es, en, pt ou uma variante regional como pt-BR, e qualquer outro valor cai para es.

O painel não permite downloads. A URL é assinada com os downloads desativados, e a resposta chega com Cache-Control: no-store: cada URL é pedida no momento de exibi-la.

Da vinculação ao painel

O percurso inteiro, na ordem em que o sistema do parceiro o realiza: resgatar o código, encontrar o atleta, ler seus resultados e exibir seu painel. Usa os mesmos dados de exemplo do restante do guia.

SHVinculação, atleta, resultados e painel
# 1. Resgatar o código de vinculação, uma única vez.
#    Guarde data.institution_id (42): é o institution_id de tudo o que vem depois.
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/link" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "EjemploDeCodigoDeVinculacion-NoEsReal_00000"}'

# 2. Encontrar o atleta por e-mail. Se vier mais de um, desempate com sex e birthdate.
#    Guarde o id dele (4821).
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/search?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "lucia.ejemplo@example.com"}'

# 3. Ler os resultados dele de um protocolo em um mês, página por página com meta.cursor.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/4821/results?institution_id=42&evaluation_type=balance_jumps_with_cea&from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"

# 4. Escolher um painel do catálogo deste ambiente: o id dele (7) não é fixado no código.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/dashboards?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"

# 5. Ao abrir a visualização, pedir a URL assinada do painel desse atleta.
curl -X POST "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes/4821/dashboards/7/embed-url?institution_id=42" \
  -H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"locale": "pt"}'
HTMLO painel na visualização do parceiro
<!-- src: o data.url devolvido pelo passo 5, como está. Expira em dez minutos. -->
<iframe
  src="URL_ASSINADA_DO_PASSO_5"
  title="Painel de Lucía Ejemplo"
  width="100%"
  height="600"
></iframe>

O que se guarda é o institution_id, o id do atleta e os resultados já lidos. A URL do painel não é guardada: ela é pedida toda vez que a visualização é aberta, e novamente se continuar aberta depois de dez minutos.

Erros e limites

Todos os erros têm o mesmo formato: status em error, uma message legível e a lista errors, cujo code é o que o sistema do parceiro deve ler. Vários desses códigos não são falhas técnicas: são estados do negócio que o sistema do parceiro precisa saber comunicar.

403A instituição não tem assinatura nem período de teste vigente
{
  "status": "error",
  "data": null,
  "message": "Institution has no active trial or subscription.",
  "errors": [
    {
      "code": "subscription_required",
      "message": "Institution has no active trial or subscription.",
      "field": null
    }
  ],
  "meta": null
}
CódigoHTTPO que aconteceuO que fazer
invalid_cursor400O cursor é inválido, expirou ou foi usado com outros filtros.Pedir a primeira página novamente.
invalid_credential401A credencial está ausente ou não funciona.Verificar a credencial em uso e o ambiente dela.
insufficient_scope403A credencial não tem o escopo que a rota exige.Consultar a Ivolution sobre o escopo.
integration_revoked403A instituição encerrou a integração de propósito.Não tentar novamente: é preciso uma nova autorização.
subscription_required403A integração existe, mas a instituição não tem assinatura nem período de teste vigente.Avisar a instituição. Isso se resolve do lado dela.
athlete_not_found404O atleta não existe, foi excluído ou é de outra instituição.Refazer a correspondência por e-mail; não tentar novamente o mesmo athlete_id.
dashboard_not_available404O painel não está no catálogo.Ler novamente o catálogo desse ambiente.
integration_not_found404Não há integração com essa instituição.Solicitar uma nova autorização.
athlete_inactive409O atleta está inativo: o painel dele não é assinado.Não exibir o painel desse atleta.
integration_expired409O código de vinculação expirou sem ser resgatado.A instituição gera um código novo.
integration_pending409A instituição autorizou, mas o código ainda não foi resgatado.Resgatar o código.
link_code_invalid409O código não existe, é de outro parceiro, já foi resgatado ou expirou.Pedir um código novo à instituição.
validation_error422A solicitação não passou na validação.Corrigir a solicitação.
rate_limited429O limite de chamadas foi excedido.Aguardar o tempo indicado em Retry-After e tentar novamente.
dashboards_unavailable503O catálogo ou o painel não podem ser confirmados agora.Tentar novamente depois de Retry-After.
results_unavailable503Os resultados não estão disponíveis no momento.Tentar novamente mais tarde, depois de Retry-After se ele vier.

Limites de uso

Os limites são contados por credencial, em três cotas independentes. Ao exceder uma, a resposta é 429 rate_limited com Retry-After.

CotaLimite
Leituras: todas as rotas exceto as duas seguintes100 por minuto
Resgate do código de vinculação (/link)10 por minuto
URL assinada de painel (/embed-url)30 por minuto

Ambiente de teste

O ambiente de teste tem uma instituição de demonstração com atletas e fichas sintéticos. Nenhum dado de uma pessoa real circula por esse ambiente, o que permite desenvolver sem acordos de confidencialidade no caminho.

Não há cadastro self-service: a credencial de teste, o institution_id da instituição de demonstração e o endereço do ambiente são entregues pela Ivolution no cadastro do parceiro. A integração com a instituição de demonstração já chega ativa, e a partir daí as rotas, as respostas e os erros são os mesmos que em produção.

Cada credencial funciona em um único ambiente. A de teste não serve em produção, nem a de produção em teste: o prefixo da credencial indica a qual ela pertence.

Responsabilidades, atribuição e exclusão

Os dados pertencem à instituição que os compartilhou, não ao parceiro nem à Ivolution. A integração implica assumir três obrigações:

  • Custódia. Os dados lidos ficam sob responsabilidade do parceiro, com o mesmo cuidado que os próprios.
  • Atribuição. Solicita-se indicar que os dados de desempenho vêm da Ivolution onde quer que sejam exibidos.
  • Exclusão a pedido. Se uma instituição revogar a integração e solicitar a exclusão do que foi lido, o parceiro deve poder fazê-lo.

Correlação por e-mail. Uma mesma pessoa pode ser atleta de duas instituições que autorizaram o parceiro: ela aparece na lista de cada uma, com um id diferente, e seus resultados são lidos separadamente, cada um com o institution_id da sua instituição. Reconhecê-la pelo e-mail é uma correlação aceita. A API, por outro lado, não cruza dados: cada leitura devolve apenas o que a instituição indicada produziu.

A revogação não apaga nada do lado do parceiro. Quando uma instituição revoga, o acesso é cortado na chamada seguinte, mas os dados já obtidos continuam no sistema do parceiro: apagá-los a pedido cabe ao parceiro.