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:
O modelo em quatro palavras
Todo o sistema se explica com quatro termos. Vale a pena lê-los antes de qualquer endpoint.
| Termo | O que é |
|---|---|
| Parceiro | A organização com acordo comercial vigente. É uma entidade do sistema, não uma pessoa nem uma instituição. |
| Credencial | O 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ção | O 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ção | O 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.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/institutions" \
-H "Authorization: Bearer $IVOLUTION_PARTNER_KEY" \
-H "Accept: application/json"{
"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.
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.
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.
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.
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.
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"}'{
"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.
| Estado | O que significa | Lê? |
|---|---|---|
pending | A instituição autorizou, mas o código ainda não foi resgatado. | Não: 409 integration_pending |
active | Resgatado. É o único estado que dá acesso. | Sim |
expired | O código expirou sem ser resgatado. Terminal. | Não: 409 integration_expired |
revoked | A 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.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/athletes?institution_id=42" \
-H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"{
"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.
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
sexebirthdateantes de guardar oid. - 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 mesmoid. - 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: oiddesportcorresponde asport_idnos resultados, e o deposition, aposition_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.
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"{
"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é oiddo atleta, egenderé o seusex, como texto; oiddesse dado chega emgender_id. - Para filtrar e agrupar, use chaves estáveis: o protocolo (
evaluation_type) eexercise_key+exercise_side. Os rótulos em espanhol, comotestouTipo_Evaluacion, podem mudar, e o nome do exercício inclui o lado. - `from` e `to` cortam pelo dia argentino (
America/Argentina/Buenos_Aires), eexerciseDatechega em UTC, comZ. Uma avaliação feita em 18 de junho às 21:22 na Argentina chega como2026-06-19T00:22:00Ze entra comto=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_typea 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.
# 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.
curl "https://app.analytics-api.ivolution.com.ar/api/v1/partner/dashboards?institution_id=42" \
-H "Authorization: Bearer $IVOLUTION_PARTNER_KEY"{
"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,inertialouphoto_cell, ounullse o painel não tiver equipamento associado. - Se o serviço de painéis não responder, a resposta é 503
dashboards_unavailable, sempre comRetry-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.
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).
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.
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.
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"}'{
"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.
# 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"}'<!-- 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.
{
"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ódigo | HTTP | O que aconteceu | O que fazer |
|---|---|---|---|
invalid_cursor | 400 | O cursor é inválido, expirou ou foi usado com outros filtros. | Pedir a primeira página novamente. |
invalid_credential | 401 | A credencial está ausente ou não funciona. | Verificar a credencial em uso e o ambiente dela. |
insufficient_scope | 403 | A credencial não tem o escopo que a rota exige. | Consultar a Ivolution sobre o escopo. |
integration_revoked | 403 | A instituição encerrou a integração de propósito. | Não tentar novamente: é preciso uma nova autorização. |
subscription_required | 403 | A 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_found | 404 | O 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_available | 404 | O painel não está no catálogo. | Ler novamente o catálogo desse ambiente. |
integration_not_found | 404 | Não há integração com essa instituição. | Solicitar uma nova autorização. |
athlete_inactive | 409 | O atleta está inativo: o painel dele não é assinado. | Não exibir o painel desse atleta. |
integration_expired | 409 | O código de vinculação expirou sem ser resgatado. | A instituição gera um código novo. |
integration_pending | 409 | A instituição autorizou, mas o código ainda não foi resgatado. | Resgatar o código. |
link_code_invalid | 409 | O código não existe, é de outro parceiro, já foi resgatado ou expirou. | Pedir um código novo à instituição. |
validation_error | 422 | A solicitação não passou na validação. | Corrigir a solicitação. |
rate_limited | 429 | O limite de chamadas foi excedido. | Aguardar o tempo indicado em Retry-After e tentar novamente. |
dashboards_unavailable | 503 | O catálogo ou o painel não podem ser confirmados agora. | Tentar novamente depois de Retry-After. |
results_unavailable | 503 | Os 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.
| Cota | Limite |
|---|---|
| Leituras: todas as rotas exceto as duas seguintes | 100 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.