Referência · v1

Uma chave, e um cabeçalho

Toda rota abaixo é HTTP com JSON, responde à mesma credencial e devolve os seus próprios identificadores. Se o seu sistema já sabe enviar uma imagem para algum lugar, ele já sabe conversar com a LumaVision.

api.lumavision.app
POST / HTTP/1.1
Host: api.lumavision.app
X-Amz-Target: RekognitionService.SearchFacesByImage
Content-Type: application/x-amz-json-1.1
Authorization: Bearer lvk_…

{ "CollectionId": "evento-2026",
  "MaxFaces": 1,
  "Image": { "Bytes": "/9j/4AAQSkZJRgABAQ…" } }

Como funciona

Você cria uma coleção, manda as suas fotos para dentro dela e depois faz perguntas. É só isso. As respostas voltam com os seus próprios identificadores, então nada aqui vira uma segunda base de dados para a sua equipe manter.

Coleção
Uma pasta. Você cria uma para cada evento, cliente ou portaria, e escolhe o nome dela. Tudo acontece dentro de uma: a busca procura ali, o agrupamento roda ali, e apagar a coleção apaga tudo o que está dentro.
external_id
O rótulo que VOCÊ dá a cada imagem que manda. Pode ser o nome do arquivo, o id da foto no seu banco, o que fizer sentido aí. Ele volta em toda resposta, para você casar com o seu registro — mas não apaga nada.
image_id
O nome que NÓS damos a cada envio. A mesma foto mandada duas vezes gera dois. É por ele que você apaga a foto depois, com todos os rostos dela.
face_id
O nome que NÓS damos a cada rosto encontrado. Uma foto com três pessoas gera três. Serve para apagar um rosto sem apagar a foto, e para perguntar por um rosto já guardado.
person_id
O nome que nós damos a uma pessoa depois de agrupar o acervo. Ele não muda: você anota no seu cadastro uma vez — “p0001 é a Maria” — e ele continua significando a Maria.
Similarity
O quanto dois rostos se parecem, de 0 a 100. Nós medimos, você decide. 80 costuma ser a mesma pessoa numa galeria de evento; numa portaria você sobe o corte, porque ali uma foto a mais machuca.

Casos de uso

Quatro coisas que as pessoas montam com esta API, na ordem em que as chamadas acontecem. Cada passo aponta para a rota que faz o trabalho.

Vender as fotos de um evento

Você fotografou um evento e quer que cada participante encontre as fotos dele sozinho, com uma selfie.

  1. 01CreateCollectionCrie uma coleção para o evento. Uma vez, no começo.
  2. 02IndexFacesMande cada foto do evento. Uma chamada por foto; a resposta é 202 e o resultado vem por webhook, então você pode mandar milhares sem segurar conexão.
  3. 03SearchFacesByImageQuando o participante manda a selfie, procure por ela na coleção. Cada resultado traz o ExternalImageId da foto em que ele aparece — os seus próprios nomes de arquivo.

Separar o acervo por pessoa, sem ninguém perguntar

Você quer montar a galeria de cada pessoa antes de alguém pedir, e não tem foto de referência de ninguém.

  1. 01ClusterMande as fotos e espere: o agrupamento roda sozinho quando elas param de chegar. Se quiser agora, peça com Mode refresh. Ele responde na hora e trabalha por alguns minutos.
  2. 02ListPeopleLeia os grupos. Cada um é uma pessoa, com quantas fotos tem e um rosto representativo para você mostrar.
  3. 03GetPersonGuarde o PersonId junto do seu cadastro. Depois, é por ele que você pergunta de novo.

Conferir quem entra

Você tem uma lista de pessoas autorizadas e uma câmera na entrada.

  1. 01IndexFacesCadastre o rosto de cada pessoa autorizada, uma vez, com MaxFaces 1. Guarde do seu lado o FaceId que o webhook devolve — é ele que apaga o rosto e pergunta por ele depois.
  2. 02AnalyzeLivenessNa entrada, mande alguns quadros da câmera. A resposta ajuda a separar uma pessoa presente da foto de uma pessoa.
  3. 03SearchFacesByImageMande o quadro com MaxFaces 1 e receba quem é. Aqui o número importa: escolha o seu FaceMatchThreshold e deixe uma pessoa decidir os casos de dúvida.

Atender um pedido de exclusão

Alguém pediu para sair do seu acervo e você precisa apagar e conseguir provar que apagou.

  1. 01ListFacesListe o que está indexado para saber o que ainda existe sobre a pessoa. Cada linha traz o ImageId da foto e o FaceId do rosto.
  2. 02DeleteImagesApague as fotos dela pelos ImageId. A resposta separa o que saiu do que já não estava lá — é o seu recibo.
  3. 03GetPersonO identificador que você tinha anotado passa a responder GONE, em vez de sumir. Assim o seu banco continua sabendo o que aconteceu.

Autenticação

O cabeçalho
Authorization: Bearer lvk_… em toda requisição. A chave começa com lvk_ e é o único jeito de provar quem chama — não há parâmetro de query, não há cookie e não há sessão.
De onde ela vem
Do cadastro: você preenche o formulário em /signup, confirma o e-mail com o código que chega nele, e a chave aparece na tela ao fim. Não há fila, não há aprovação e não há uma rota de API para isso — a conta nasce no navegador.
Onde ela vive
Guardamos apenas o hash da sua chave, então ela é mostrada uma vez, quando é emitida, e não é legível depois. Perdeu, revoga e emite outra: não existe recuperar.
O que ela alcança
Só o seu espaço. Uma coleção que não é sua não responde “sem permissão”, ela simplesmente não existe para a sua chave — a fronteira está na consulta, não numa verificação que alguém possa esquecer de escrever.
Erros comuns
  • 401 Chave ausente, revogada ou de uma conta inativa.
  • 404 O recurso não existe para a sua chave.
  • 422 O corpo não bate com o contrato. A resposta diz qual campo e por quê.
  • 429 Teto de chamadas por segundo, ou a cota do mês. O corpo traz o limite e quando ele volta; `Retry-After` também.

O que a API aceita

Tamanho do arquivo

5 MB · 25 MB

Padrão de 5 MB por imagem nas ações que indexam, buscam ou checam prova de vida, e 25 MB em DetectFaces, que existe para medir uma fotografia inteira. O teto vale sobre os bytes DECODIFICADOS: base64 infla cerca de um terço, então uma string de 6,6 MB não passa num teto de 5 MB.

Dimensão da imagem

8000 px · 10000 px

Padrão do maior lado, nas mesmas duas famílias de ação. Uma imagem acima disso é recusada, nunca reduzida em silêncio.

Corpo da requisição

40 MB

O total de uma chamada, o que importa quando ela carrega vários quadros de uma vez. É recusado antes de o corpo ser lido inteiro.

Rosto mínimo

32 px

O lado da caixa do rosto. Abaixo disso o rosto é descartado — e é por isso que uma foto de 8000 px com um rosto de 30 px é recusada pelo rosto, não pela foto.

O que vai para o modelo

2560 px

Na fila (IndexFaces) uma imagem enviada em Bytes é reduzida para 2560 px no lado maior e reencodada antes de chegar ao modelo, que trabalha nessa escala — pixels acima disso só custariam upload. Nas ações síncronas os bytes vão como vieram. Em nenhuma delas a sua cópia é alterada: um pedido de exclusão fala do arquivo do seu cliente, e ele fica com você.

O que decide é o rosto

Não o tamanho da imagem. Uma foto grande com gente pequena e distante rende rostos pequenos; uma foto modesta com a pessoa em primeiro plano rende um rosto grande. É a segunda que responde melhor.

Os tetos são por conta e podem ser ampliados. Quando um é atingido, a resposta diz qual limite e qual valor estava em vigor — que é a única cópia de um limite que não envelhece.

O fio da Rekognition

Um cliente da Amazon Rekognition troca o endpoint e a chave e continua igual: POST / com o header X-Amz-Target: RekognitionService.<Ação>, corpo e resposta em JSON com os nomes da AWS, assinatura SigV4 (AccessKeyId é o prefixo da sua chave; SecretAccessKey é a chave) — ou Authorization: Bearer, se você não usa o SDK. As ações marcadas LV são só nossas e seguem o mesmo padrão; campos marcados como extensão precisam do modelo LumaVision para o botocore ou de HTTP direto, porque o cliente da AWS recusa o que não está no modelo dela.

POST/ X-Amz-Target: RekognitionService.CreateCollection

Cria uma coleção

Igual ao `CreateCollection` da AWS. A coleção nunca nasce sozinha: mandar imagem para uma chave desconhecida é erro, não criação implícita. Criar uma que já existe é `ResourceAlreadyExistsException`. O identificador é seu — use a chave que já significa alguma coisa no seu sistema, porque é ela que volta em toda resposta.

Na requisição

CollectionIdstringobrigatório1 a 255
A chave, sua. Letras, números, ponto, dois-pontos, hífen e sublinhado.
Tagsobject
Rótulos texto → texto, guardados e devolvidos por `DescribeCollection`. Nunca interpretados.

Na resposta

StatusCodeinteger
Sempre 200.
CollectionArnstring
Um nome com forma de ARN; o segmento de conta é o seu tenant.
FaceModelVersionstring
O rótulo público do modelo com que a conta indexa.
Requisição
{
  "CollectionId": "evento-2026",
  "Tags": {"cliente": "Colegio Vieira"}
}
200
{
  "StatusCode": 200,
  "CollectionArn": "arn:lumavision:rekognition::1:collection/evento-2026",
  "FaceModelVersion": "lumavision-v5"
}

Criada.

ResourceAlreadyExistsException

Já existe uma coleção com essa chave nesta conta.

POST/ X-Amz-Target: RekognitionService.ListCollections

Lista as coleções da conta

Igual ao `ListCollections` da AWS: as coleções, da mais nova para a mais antiga, paginadas por `NextToken`. `FaceModelVersions` acompanha `CollectionIds` posição a posição, como na AWS — toda coleção de uma conta indexa com o mesmo modelo, então as entradas repetem.

Na requisição

MaxResultsinteger0 a 4096; 0 ou ausente usa a página padrão (1000)
Quantas coleções por página.
NextTokenstringaté 255 caracteres
O token da página anterior. Opaco: guarde e devolva como veio. Um token que não decodifica é `InvalidPaginationTokenException`.

Na resposta

CollectionIdsstring[]
Os ids das coleções desta página, como você os criou.
FaceModelVersionsstring[]
Um rótulo de modelo por coleção, na mesma ordem.
NextTokenstring
Presente quando há mais páginas; ausente na última.
Requisição
{
  "MaxResults": 100
}
200
{
  "CollectionIds": ["evento-2026", "formatura-2025"],
  "FaceModelVersions": ["lumavision-v5", "lumavision-v5"],
  "NextToken": "eyJ0IjoiMjAyNi0wOS0wNVQxMDo1ODowMy4zNjVaIiwiaWQiOiJ0MTpmb3JtYXR1cmEtMjAyNSJ9"
}

Uma página. Sem `NextToken` é a última.

POST/ X-Amz-Target: RekognitionService.DescribeCollection

Quantas faces tem aqui

Igual ao `DescribeCollection` da AWS. A pergunta de suporte mais comum — “mandei 4.000 fotos, entraram?” — em faces, não em fotos: uma foto com 40 pessoas conta 40. Repare na grafia `CollectionARN`, diferente de `CreateCollection`: é a da AWS.

Na requisição

CollectionIdstringobrigatório
A coleção.

Na resposta

FaceCountinteger
Faces indexadas e não apagadas.
FaceModelVersionstring
O rótulo público do modelo.
CollectionARNstring
O mesmo nome que `CreateCollection` devolveu como `CollectionArn`.
CreationTimestampdatetime
Quando a coleção foi criada.
UserCountinteger
Sempre 0: não há associação usuário↔face aqui.
Tagsobject
O que `CreateCollection` gravou em `Tags`; ausente quando nada foi gravado.
Requisição
{
  "CollectionId": "evento-2026"
}
200
{
  "FaceCount": 252014,
  "FaceModelVersion": "lumavision-v5",
  "CollectionARN": "arn:lumavision:rekognition::1:collection/evento-2026",
  "CreationTimestamp": "2026-08-14T13:02:11.481902+00:00",
  "UserCount": 0,
  "Tags": {"cliente": "Colegio Vieira"}
}

A contagem e os metadados.

ResourceNotFoundException

A coleção não existe nesta conta.

POST/ X-Amz-Target: RekognitionService.DeleteCollection

Apaga uma coleção e tudo dentro

Igual ao `DeleteCollection` da AWS: a coleção e cada face nela, e responde só o status. Apagar uma que não existe é `ResourceNotFoundException`. Se você precisa das contagens como recibo, `DeleteCollections` com um id só faz a mesma exclusão com o recibo completo.

Na requisição

CollectionIdstringobrigatório
A coleção.

Na resposta

StatusCodeinteger
Sempre 200.
Requisição
{
  "CollectionId": "evento-2026"
}
200
{ "StatusCode": 200 }

Apagada.

ResourceNotFoundException

A coleção não existe — ou já foi apagada.

POST/ X-Amz-Target: LumaVision.DeleteCollections

Apaga várias coleções, com recibo

Nossa: o cliente inteiro sai, vários eventos de uma vez, até 100 por chamada. Cada coleção vai na própria transação e o recibo separa o que existia do que não — o que um pedido de exclusão precisa como prova.

Na requisição

CollectionIdsstring[]obrigatório1 a 100
As coleções. Repetidas colapsam.

Na resposta

Deletedstring[]
As que existiam e foram apagadas.
NotFoundstring[]
As que não existiam.
FacesDeletedinteger
Quantas faces saíram com elas.
PeopleDeletedinteger
Quantos agrupamentos de pessoa saíram junto.
Requisição
{
  "CollectionIds": ["evento-2026", "turma-2019"]
}
200
{
  "Deleted": ["evento-2026"],
  "NotFound": ["turma-2019"],
  "FacesDeleted": 252014,
  "PeopleDeleted": 8432
}

O recibo.

POST/ X-Amz-Target: RekognitionService.ListFaces

O que está indexado numa coleção

Igual ao `ListFaces` da AWS: blocos `Face` nus, do mais antigo para o mais novo, paginados por `NextToken`. É a resposta a “o que vocês guardam sobre mim?” e o plano de contingência para um `ImageId` que você não gravou. `FaceIds` é o filtro da AWS; `PersonId` é o nosso — as fotos de uma pessoa que o agrupamento encontrou.

Na requisição

CollectionIdstringobrigatório
A coleção.
MaxResultsinteger0 a 4096; padrão 1000
Quantas faces por página.
NextTokenstring
O token da página anterior.
FaceIdsstring[]1 a 4096
Só estas faces.
PersonIdstring
Só as faces desta pessoa. Uma pessoa que existe e não tem faces aqui devolve página vazia; uma que não existe é `ResourceNotFoundException`.

Na resposta

Facesobject[]
Blocos `Face`: `FaceId`, `BoundingBox`, `ImageId`, `ExternalImageId`, `Confidence`, `IndexFacesModelVersion` e o nosso `model`.
NextTokenstring
Presente quando há mais páginas.
FaceModelVersionstring
O rótulo do modelo com que a coleção indexa hoje.
Requisição
{
  "CollectionId": "evento-2026",
  "MaxResults": 100
}
200
{
  "Faces": [
    {
      "FaceId": "019fe6fa-b9bb-7da2-8a76-5d834cc7a6b4",
      "BoundingBox": {"Top": 0.20, "Left": 0.44, "Width": 0.18, "Height": 0.21},
      "ImageId": "019fe6fa-b9bb-7315-b71a-4201946bdb44",
      "ExternalImageId": "IMG_2291",
      "Confidence": 99.87,
      "model": "lumavision-v5"
    }
  ],
  "FaceModelVersion": "lumavision-v5"
}

Uma página.

ResourceNotFoundException

A coleção não existe, ou o `PersonId` não é de nenhuma pilha desta conta.

POST/ X-Amz-Target: RekognitionService.DeleteFaces

Apaga faces pelos ids

Igual ao `DeleteFaces` da AWS: só estas linhas, até 4096 por chamada — o rosto de uma criança numa foto de turma, e a foto continua servindo os outros. Idempotente: um id que já saiu volta em `UnsuccessfulFaceDeletions` com `FACE_NOT_FOUND`. Quem guardou só o `FaceId` — a foto de referência de um cliente, por exemplo — pode mandar os ids sem `CollectionId`.

Na requisição

CollectionIdstring
A coleção. Opcional aqui (na AWS é obrigatório): sem ela, cada `FaceId` é apagado na coleção da sua conta onde estiver. Uma face de outra conta responde `FACE_NOT_FOUND`, igual a um id que ninguém tem, e não é tocada. O SDK da AWS puro exige o campo: para omiti-lo, use o modelo LumaVision ou HTTP direto.
FaceIdsstring[]obrigatório1 a 4096
Os `FaceId` que a indexação devolveu.

Na resposta

DeletedFacesstring[]
Os ids que existiam e foram apagados.
UnsuccessfulFaceDeletionsobject[]
Cada id não apagado, com `Reasons` — aqui sempre `FACE_NOT_FOUND`.
PeopleDeletedinteger
Agrupamentos de pessoa que esvaziaram com estas faces.
Requisição
{
  "CollectionId": "evento-2026",
  "FaceIds": ["019fe821-9b6b-70da-b15e-0717be628dfb"]
}
200
{
  "DeletedFaces": ["019fe821-9b6b-70da-b15e-0717be628dfb"],
  "UnsuccessfulFaceDeletions": [],
  "PeopleDeleted": 0
}

O que saiu e o que não estava lá.

POST/ X-Amz-Target: LumaVision.DeleteImages

Apaga fotos inteiras pelos ImageId

Nossa, e o desvio deliberado da AWS: “apaguem aquela foto” é o pedido que chega, e um `ImageId` nomeia exatamente um envio — todas as faces dele saem, sem você precisar saber quantas eram. Até 4096 por chamada; o recibo é por foto, com a contagem de faces ao lado.

Na requisição

CollectionIdstringobrigatório
A coleção.
ImageIdsstring[]obrigatório1 a 4096
Os `image_id` que a indexação devolveu.

Na resposta

DeletedImagesstring[]
As fotos que existiam e foram apagadas.
NotFoundstring[]
As que não estavam lá.
FacesDeletedinteger
Quantas faces saíram — uma foto pode ter várias.
PeopleDeletedinteger
Agrupamentos de pessoa que esvaziaram junto.
Requisição
{
  "CollectionId": "evento-2026",
  "ImageIds": ["019fe821-995a-745e-9e84-2d6969390961"]
}
200
{
  "DeletedImages": ["019fe821-995a-745e-9e84-2d6969390961"],
  "NotFound": [],
  "FacesDeleted": 3,
  "PeopleDeleted": 0
}

O recibo.

POST/ X-Amz-Target: LumaVision.UpdateExternalImageId

Troca o ExternalImageId de fotos já indexadas

Nossa, sem equivalente na AWS: reescreve o seu rótulo em todas as faces de cada foto, endereçada pelo `ImageId` — nada é reprocessado, nada vai para a GPU, nenhum webhook sai. O rótulo continua sendo só um rótulo: ninguém é encontrado por ele, nem aqui. Até 4096 fotos por chamada; repetir a chamada dá a mesma resposta.

Na requisição

CollectionIdstringobrigatório
A coleção.
Imagesobject[]obrigatório1 a 4096
Pares `{ImageId, ExternalImageId}`: o `image_id` que a indexação devolveu e o rótulo novo (1 a 255 caracteres). A mesma foto duas vezes é recusada.

Na resposta

UpdatedFacesobject[]
Cada face reescrita: `FaceId`, `ImageId` e o `ExternalImageId` novo.
NotFoundstring[]
As fotos que a coleção não tem — apagadas, de outra conta, ou ainda na fila (essas ficam com o rótulo do envio).
Requisição
{
  "CollectionId": "referencias",
  "Images": [{ "ImageId": "019fe821-995a-745e-9e84-2d6969390961", "ExternalImageId": "pessoa-1042" }]
}
200
{
  "UpdatedFaces": [{
    "FaceId": "019fe821-9b6b-70da-b15e-0717be628dfb",
    "ImageId": "019fe821-995a-745e-9e84-2d6969390961",
    "ExternalImageId": "pessoa-1042"
  }],
  "NotFound": []
}

O que foi reescrito.

InvalidParameterException

A mesma foto apareceu duas vezes em `Images` (`lumavision.faces.duplicate_image_id`).

POST/ X-Amz-Target: RekognitionService.SearchFaces

Onde esta face que já indexei aparece

Igual ao `SearchFaces` da AWS: a sonda é uma face que o serviço já tem, pelo `FaceId`. Uma chamada, uma ou muitas coleções, uma lista ordenada do mais parecido para o menos e cortada em `FaceMatchThreshold`. A própria sonda volta quando a coleção dela está entre as buscadas — você nomeou aquela coleção — e é o único casamento que lê `Similarity` 100; duas fotos da mesma pessoa ficam abaixo disso. Toda busca já alcança outras fotos da mesma pessoa em poses e luzes diferentes; não há modo rápido para escolher.

Na requisição

FaceIdstringobrigatório
O `FaceId` de uma face indexada nesta conta. Vai sozinho: ele já é inequívoco.
CollectionIdstring
A coleção, como na AWS. Ou `CollectionIds`; nunca os dois.
CollectionIdsstring[]1 a 200
Várias coleções numa chamada; cada casamento diz de qual veio.
MaxFacesinteger1 a 4096; sem valor, desce até caber 100.000 linhas
Teto por coleção. Não é a pergunta: o corte é `FaceMatchThreshold`.
FaceMatchThresholdnumber0 a 100, padrão 80
O corte, na escala publicada. 80 costuma ser a mesma pessoa numa galeria de evento.

Na resposta

SearchedFaceIdstring
A sonda, de volta.
FaceMatchesobject[]
Os casamentos, do mais parecido para o menos. Cada um traz `Similarity` (0–100) e o `Face` que casou — o mesmo bloco `Face` da indexação e da listagem — mais as extensões `CollectionId`, `PersonId`, `Pose`, `Position` e `PositionScore`.
FaceModelVersionstring
O rótulo do modelo que respondeu. Um só: a busca não cruza espaços de embedding.
VectorQueriesinteger
Consultas vetoriais que a chamada pagou.
Truncatedboolean
`true` quando `MaxFaces` cortou a resposta de alguma coleção.
Requisição
{
  "CollectionIds": ["evento-2026", "colacao-2026"],
  "FaceId": "019fe870-467d-7d49-a639-a94ef3db3915",
  "FaceMatchThreshold": 80
}
200
{
  "SearchedFaceId": "019fe870-467d-7d49-a639-a94ef3db3915",
  "FaceMatches": [
    {
      "Similarity": 99.93,
      "Face": {
        "FaceId": "019fe871-8af1-78b6-a57a-b7cfd33fcc10",
        "BoundingBox": {"Top": 0.11, "Left": 0.44, "Width": 0.15, "Height": 0.17},
        "ImageId": "019fe871-8af0-78a9-8d98-e082907efbde",
        "ExternalImageId": "DSC_0902",
        "Confidence": 99.41
      },
      "CollectionId": "colacao-2026",
      "Pose": {"Yaw": -10.8, "Roll": -4.0, "Pitch": 5.1},
      "PersonId": "t1:p000117"
    }
  ],
  "FaceModelVersion": "lumavision-v5",
  "VectorQueries": 4,
  "Truncated": false
}

Os casamentos acima do corte, do mais parecido para o menos.

ResourceNotFoundException

Alguma coleção não existe, ou o `FaceId` não é seu — o mesmo erro de propósito.

POST/ X-Amz-Target: RekognitionService.SearchFacesByImage

Onde esta foto aparece

Igual ao `SearchFacesByImage` da AWS: a sonda é a maior face da imagem. Um rosto de perfil é recusado, porque uma busca de perfil não falha — devolve resultados confiantes e errados. Não há modo rápido para escolher. Para “quem é esta pessoa”, `MaxFaces: 1`.

Na requisição

Imageobjectobrigatório
`{"Bytes": <base64>}` como na AWS, ou `{"Url": ...}` (extensão). `S3Object` é recusado: não lemos buckets de terceiros.
QualityFilterstring
Aceito e ignorado: o portão de pose é o filtro de qualidade daqui.
CollectionIdstring
A coleção, como na AWS. Ou `CollectionIds`; nunca os dois.
CollectionIdsstring[]1 a 200
Várias coleções numa chamada; cada casamento diz de qual veio.
MaxFacesinteger1 a 4096; sem valor, desce até caber 100.000 linhas
Teto por coleção. Não é a pergunta: o corte é `FaceMatchThreshold`.
FaceMatchThresholdnumber0 a 100, padrão 80
O corte, na escala publicada. 80 costuma ser a mesma pessoa numa galeria de evento.

Na resposta

SearchedFaceBoundingBoxobject
Onde estava a face usada como sonda — a maior da foto.
SearchedFaceConfidencenumber
Quão certo o detector estava dela, 0–100.
FaceMatchesobject[]
Os casamentos, do mais parecido para o menos. Cada um traz `Similarity` (0–100) e o `Face` que casou — o mesmo bloco `Face` da indexação e da listagem — mais as extensões `CollectionId`, `PersonId`, `Pose`, `Position` e `PositionScore`.
FaceModelVersionstring
O rótulo do modelo que respondeu. Um só: a busca não cruza espaços de embedding.
VectorQueriesinteger
Consultas vetoriais que a chamada pagou.
Truncatedboolean
`true` quando `MaxFaces` cortou a resposta de alguma coleção.
Requisição
{
  "CollectionId": "evento-2026",
  "Image": {"Bytes": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q=="},
  "MaxFaces": 1
}
200
{
  "SearchedFaceBoundingBox": {"Top": 0.18, "Left": 0.33, "Width": 0.31, "Height": 0.44},
  "SearchedFaceConfidence": 99.92,
  "FaceMatches": [
    {
      "Similarity": 99.71,
      "Face": {
        "FaceId": "019fe871-4e3c-7a1e-9f1a-2c2b7d3b9c11",
        "BoundingBox": {"Top": 0.16, "Left": 0.29, "Width": 0.41, "Height": 0.47},
        "ImageId": "019fe871-4e3c-736d-904e-a908d2ec1d66",
        "ExternalImageId": "DSC_0817",
        "Confidence": 99.87
      },
      "CollectionId": "evento-2026",
      "Pose": {"Yaw": -1.0, "Roll": -0.5, "Pitch": 0.2},
      "PersonId": null
    }
  ],
  "FaceModelVersion": "lumavision-v5",
  "VectorQueries": 1,
  "Truncated": false
}

Os casamentos. Coleção sem ninguém parecido é `FaceMatches: []`, nunca um erro.

PoseNotFrontalException

Rosto fora do corte de pose. A mensagem diz o eixo e os graus.

InvalidParameterException

Nenhuma face na imagem (`lumavision.faces.no_face`), ou os dois formatos de coleção ao mesmo tempo.

POST/ X-Amz-Target: RekognitionService.SearchPersonFaces

Encontra uma pessoa e devolve sua galeria

Envie uma imagem ou uma `FaceId`, em uma coleção ou várias. Em CADA coleção pedida o serviço faz a mesma busca do `SearchFaces`, pega o rosto mais parecido e, se ele pertence a uma pessoa, devolve as faces daquela pessoa nessa coleção que passam no seu `FaceMatchThreshold`. As pessoas são por coleção, então uma chamada com várias coleções responde uma pessoa por coleção, em `Persons`. É a chamada para abrir a galeria da pessoa a partir de uma selfie. Uma coleção sem casamento, ou cujo rosto mais parecido ainda não pertence a ninguém, responde `PersonId` nulo e não contribui com faces — a não ser que você peça `NeighboursWhenUnclustered`.

Na requisição

CollectionIdstring
A coleção. Ou `CollectionIds`; nunca os dois.
CollectionIdsstring[]1 a 200
Várias coleções numa chamada. Cada uma é resolvida por conta própria: a sua pessoa naquela coleção, e as faces dela ali.
MaxFacesinteger1 a 4096; padrão 4096
Máximo de faces da galeria, por coleção. Quando cortar, `Truncated` vem como `true`.
FaceMatchThresholdnumber0 a 100, padrão 80
O corte da galeria: só voltam as faces da pessoa com `Similarity` à sua sonda acima dele. Quem a pessoa é, quem decide é o serviço; quais fotos dela voltam, você.
NeighboursWhenUnclusteredbooleanpadrão false
Numa coleção em que o rosto mais parecido não pertence a ninguém (ainda não agrupada, por exemplo), responde com a busca comum — a do `SearchFaces`, com este corte e este `MaxFaces` —, cada casamento com `PersonId: null`. Desligado, essa coleção não devolve nada.
FaceIdstring
Uma face sua já indexada. Envie esta ou `Image`, nunca as duas.
Imageobject
`{"Bytes": <base64>}` ou `{"Url": ...}`. Envie esta ou `FaceId`, nunca as duas.

Na resposta

PersonIdstring
A pessoa da coleção em que o rosto mais parecido estava mais perto da sonda, ou `null` se nenhuma pessoa foi encontrada. Com uma coleção só, é a pessoa dela.
Personsobject[]
Uma entrada por coleção pedida, na ordem do pedido: `CollectionId` e `PersonId` (`null` onde a sonda não resolveu para uma pessoa).
FaceMatchesobject[]
As faces dessas pessoas acima do corte, todas as coleções juntas, ordenadas pela semelhança com a sonda. Cada uma traz `Similarity`, `Face`, `CollectionId`, `PersonId`, `Pose`, `Position` e `PositionScore`.
FaceModelVersionstring
O rótulo do modelo que respondeu.
VectorQueriesinteger
Consultas vetoriais usadas para encontrar a pessoa em cada coleção, somadas; a leitura da galeria não acrescenta consultas.
Truncatedboolean
`true` quando `MaxFaces` cortou a galeria de alguma coleção.
Requisição
{
  "CollectionIds": ["evento-2026", "evento-2026-dia2"],
  "Image": {"Bytes": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q=="},
  "MaxFaces": 200
}
200
{
  "PersonId": "t1:p000117",
  "Persons": [
    {"CollectionId": "evento-2026", "PersonId": null},
    {"CollectionId": "evento-2026-dia2", "PersonId": "t1:p000117"}
  ],
  "FaceMatches": [
    {
      "Similarity": 99.93,
      "Face": {
        "FaceId": "019fe871-8af1-78b6-a57a-b7cfd33fcc10",
        "BoundingBox": {"Top": 0.11, "Left": 0.44, "Width": 0.15, "Height": 0.17},
        "ImageId": "019fe871-8af0-78a9-8d98-e082907efbde",
        "ExternalImageId": "DSC_0902",
        "Confidence": 99.41
      },
      "CollectionId": "evento-2026-dia2",
      "Pose": {"Yaw": -10.8, "Roll": -4.0, "Pitch": 5.1},
      "PersonId": "t1:p000117"
    }
  ],
  "FaceModelVersion": "lumavision-v5",
  "VectorQueries": 4,
  "Truncated": false
}

A pessoa em cada coleção e a galeria dela, ou uma galeria vazia onde a sonda não resolve para uma pessoa.

ResourceNotFoundException

Alguma coleção não existe, ou a `FaceId` não é sua.

InvalidParameterException

Os dois formatos de coleção, as duas sondas, ou nenhuma sonda foram enviados.

PoseNotFrontalException

A imagem enviada não passou pelo corte de pose.

POST/ X-Amz-Target: RekognitionService.DetectFaces

Mede uma imagem sem guardar nada

Igual ao `DetectFaces` da AWS: todo rosto da imagem e tudo o que os modelos mediram sobre ele. Nada é indexado e nada é endereçado depois — por isso só os blocos de medição, sem `FaceId`. Usa o teto de upload maior, porque quem pergunta o que tem numa fotografia manda a fotografia inteira.

Na requisição

Imageobjectobrigatório
`{"Bytes": <base64>}` ou `{"Url": ...}`.
Attributesstring[]
Aceito e ignorado: o serviço mede sempre o que o pipeline mede.

Na resposta

FaceDetailsobject[]
Um `FaceDetail` por rosto: `BoundingBox`, `Pose`, `Quality`, `Landmarks`, `Confidence` e os atributos que o pipeline mediu.
Requisição
{
  "Image": {"Bytes": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q=="}
}
200
{
  "FaceDetails": [
    {
      "BoundingBox": {"Top": 0.16, "Left": 0.29, "Width": 0.41, "Height": 0.47},
      "Pose": {"Yaw": -1.0, "Roll": -0.5, "Pitch": 0.2},
      "Quality": {"Sharpness": 54.2, "Brightness": 23.2},
      "Landmarks": [{"Type": "eyeLeft", "X": 0.40, "Y": 0.34}],
      "Confidence": 94.5
    }
  ]
}

As medições, e nada gravado.

POST/ X-Amz-Target: RekognitionService.IndexFaces

Indexa os rostos de uma foto, pela fila

O `IndexFaces` da AWS com uma diferença que você precisa saber antes de migrar: a AWS responde 200 com as faces dentro; aqui a resposta é 202 com `FaceRecords` vazio e o envelope preenchido chega por webhook, um por foto. É o que permite mandar milhares de fotos sem segurar conexão. Guarde o `ImageId` do 202 — ele já apaga a submissão antes mesmo de o webhook chegar — e, do webhook, cada `FaceId`. Uma repetição com o mesmo `amz-sdk-invocation-id` responde a mesma submissão e não é cobrada de novo.

Na requisição

CollectionIdstringobrigatório
Onde os rostos entram. Precisa existir; uma chave desconhecida é `ResourceNotFoundException` antes de qualquer byte ser guardado.
Imageobjectobrigatório
`{"Bytes": <base64>}` como na AWS, ou `{"Url": ...}` (extensão). `S3Object` é recusado: não lemos buckets de terceiros.
ExternalImageIdstring
O seu rótulo para a imagem. Volta no webhook e em toda resposta que nomeia um rosto dela. Metadado, nunca chave: não é único, não filtra e não apaga.
MaxFacesintegermínimo 1, sem teto
Só as N maiores faces da foto são indexadas. Para cadastrar uma pessoa a partir de uma selfie, 1.
QualityFilterstring
Aceito e ignorado: o corte de qualidade é o do pipeline.
DetectionAttributesstring[]
Aceito e ignorado: todo atributo medido viaja no webhook de qualquer forma.
WebhookEndpointIdinteger
Para qual dos seus endpoints entregar. Sem ele, o endpoint ativo da conta. Um id inexistente, inativo ou de outra conta é `ResourceNotFoundException` na porta.

Na resposta

FaceRecordsobject[]
Sempre vazio no 202: as faces chegam pelo webhook, no mesmo envelope.
FaceModelVersionstring
O rótulo público do modelo com que a conta indexa.
ImageIdstring
A submissão, cunhada agora. É o endereço de apagamento; o SDK da AWS puro descarta este membro — use o modelo LumaVision ou HTTP cru para lê-lo.
RequestIdinteger
A linha da fila. Volta no webhook e liga a chamada às entregas em `GetCall`.
Requisição
{
  "CollectionId": "evento-2026",
  "ExternalImageId": "IMG_2291",
  "Image": { "Url": "https://storage.seu-estudio.com.br/evento-2026/IMG_2291.jpg?X-Amz-Expires=3600&X-Amz-Signature=9b1c0e7a4f" }
}
202
{
  "FaceRecords": [],
  "FaceModelVersion": "lumavision-v3",
  "ImageId": "019fe870-467d-7432-9904-1fc838cf68a4",
  "RequestId": 918273
}

Aceita. Não é o resultado — o resultado chega por webhook, um por imagem.

POST/ X-Amz-Target: LumaVision.ListJobs

Os trabalhos da sua chave

Do mais novo para o mais antigo, paginado por cursor — “o que está rodando”, “o que falhou hoje”, “o lote da manhã terminou?”. Um trabalho é um lote de imagens do `IndexFaces` (`Kind` process) ou um agrupamento (cluster). Os filtros são enumerações: um valor desconhecido é `InvalidParameterException`, nunca uma página vazia, porque uma página vazia por erro de digitação leria como “nada falhou”.

Na requisição

Statusstring
Só os trabalhos nesse estado: `PENDING_SUBMIT`, `DISPATCHED`, `QUEUED`, `RUNNING`, `INGESTED`, `FAILED` ou `CANCELLED`.
Kindstring
Só os trabalhos desse tipo: `process` ou `cluster`.
IncludeSupersededbooleanpadrão false
Se as tentativas que já foram substituídas por um retry também aparecem.
MaxResultsinteger0 a 4096; padrão 1000
Quantos por página.
NextTokenstring
O token da página anterior.

Na resposta

Jobsobject[]
Uma página. `INGESTED` é o terminal feliz: o resultado foi gravado e o webhook, enviado.
NextTokenstring
Presente quando há mais páginas; ausente na última.
Requisição
{ "Status": "FAILED", "MaxResults": 50 }
200
{
  "Jobs": [
    {
      "JobId": 918273,
      "Kind": "process",
      "Mode": "index_only",
      "Status": "FAILED",
      "Error": "lumavision.queue.no_result",
      "DispatchedAt": "2026-08-07T03:17:00Z"
    }
  ]
}

Uma página.

POST/ X-Amz-Target: LumaVision.GetJob

O estado de um trabalho

Para reconciliação, não para polling: o resultado de um trabalho chega por webhook, e esta ação diz em que pé ele está quando o webhook não chegou. Um trabalho de outra conta é `ResourceNotFoundException`, igual a um que não existe — “este id existe mas não é seu” já é uma resposta. Campo sem valor some: `CollectionId` não aparece num lote de imagens, `Error` só aparece num trabalho que falhou.

Na requisição

JobIdintegerobrigatório
O identificador do trabalho.

Na resposta

JobIdinteger
O identificador.
Kindstring
`process` ou `cluster`.
Modestring
`index_only` num lote; `bootstrap` ou `refresh` num agrupamento.
Statusstring
Pare de consultar quando não estiver em `QUEUED`, `RUNNING`, `PENDING_SUBMIT` ou `DISPATCHED`.
CollectionIdstring
A coleção de um agrupamento. Um lote de imagens não pertence a uma coleção — as imagens dele pertencem, cada uma à sua.
Errorstring
Por que falhou, quando falhou.
RetryOfinteger
A tentativa que esta substitui, quando nasceu de um `RetryJob`.
DispatchedAtdatetime
Quando foi despachado.
IngestedAtdatetime
Quando terminou.
Requisição
{ "JobId": 918273 }
200
{
  "JobId": 918273,
  "Kind": "cluster",
  "Mode": "bootstrap",
  "Status": "INGESTED",
  "CollectionId": "evento-2026",
  "DispatchedAt": "2026-08-07T03:17:00Z",
  "IngestedAt": "2026-08-07T03:19:42Z"
}

O estado.

POST/ X-Amz-Target: LumaVision.CancelJob

Para um trabalho em andamento

A resposta diz o que parar SIGNIFICOU, porque a verdade difere por caso: um agrupamento para no próximo checkpoint; um lote ainda não despachado nem começa, e as imagens dele são liberadas; um lote que já está na GPU não pode ser chamado de volta — ele roda até o fim e o resultado é descartado quando chega. Um trabalho que já terminou é `InvalidParameterException`: não havia o que parar, e um 200 diria que havia.

Na requisição

JobIdintegerobrigatório
O trabalho a parar.

Na resposta

JobIdinteger
O identificador.
Kindstring
`process` ou `cluster`.
Statusstring
Sempre `CANCELLED`.
ReleasedImagesinteger
Quantas imagens suas saíram do lote, com o upload estacionado apagado na hora. Zero num agrupamento.
Notestring
A frase que diz o que parar quis dizer neste caso.
Requisição
{ "JobId": 918273 }
200
{
  "JobId": 918273,
  "Kind": "process",
  "Status": "CANCELLED",
  "ReleasedImages": 12,
  "Note": "the job had not been submitted yet, so nothing was started"
}

Parado — e a frase que diz o que isso quis dizer.

POST/ X-Amz-Target: LumaVision.RetryJob

Roda de novo um trabalho que falhou

Só a partir de FAILED — um trabalho rodando ainda não falhou, e um cancelado foi parado de propósito. A tentativa que falhou fica como estava, porque ela é a evidência do que deu errado: um agrupamento ganha um trabalho novo apontando para ela (`JobId`), e as imagens de um lote voltam para a fila e respondem cada uma no seu webhook (sem `JobId`, porque elas vão cair em lotes novos). Se o insumo não existe mais — o upload já foi liberado, a coleção foi apagada — a resposta diz isso agora em vez de falhar de novo em minutos.

Na requisição

JobIdintegerobrigatório
O trabalho que falhou.

Na resposta

RetryOfinteger
O trabalho que falhou.
Kindstring
`process` ou `cluster`.
JobIdinteger
O trabalho novo, num agrupamento. Ausente num lote: o lote seguinte é quem cria o envelope.
RequeuedImagesinteger
Quantas imagens voltaram para a fila. Zero num agrupamento.
Notestring
O que aconteceu, em palavras.
Requisição
{ "JobId": 918190 }
200
{
  "RetryOf": 918190,
  "Kind": "cluster",
  "JobId": 918301,
  "RequeuedImages": 0,
  "Note": "a new run was queued; the failed attempt is kept as it was"
}

Um agrupamento: o novo trabalho já está na fila.

200
{
  "RetryOf": 918273,
  "Kind": "process",
  "RequeuedImages": 12,
  "Note": "the images were put back on the queue and will ride a new batch; each answers on its own webhook"
}

Um lote de imagens: elas voltaram para a fila.

POST/ X-Amz-Target: LumaVision.Cluster

Agrupa o acervo por pessoa

Sem foto de referência e sem ninguém para perguntar. Você normalmente não precisa chamar: toda coleção é agrupada sozinha pouco depois de parar de receber imagens, e de novo a cada leva nova — cada pessoa que já existe procura entre as faces novas as que são dela, e só o que ninguém alcançou vira pessoa nova. Esta ação é para pedir um agrupamento agora, ou num escopo maior. O trabalho leva minutos e roda um de cada vez, então a resposta é sempre 202. Se já há um agrupamento a caminho que vai cobrir todas as faces indexadas até aqui, a resposta é ele mesmo, não um segundo; senão, o pedido entra na fila e a resposta diz quantos estão na frente. Os grupos aparecem em `ListPeople`, e um webhook `Cluster.Completed` avisa quando um agrupamento criou ou aumentou alguma pessoa — com só as pessoas que mudaram; um que não mudou ninguém termina em silêncio. `bootstrap` numa coleção que já tem pessoas é `ResourceAlreadyExistsException`: reemitir identificadores que você já gravou não é uma operação que este serviço faça.

Na requisição

CollectionIdstringobrigatório
A coleção âncora: toda pessoa do run é arquivada nela.
Modestringpadrão bootstrap
O mesmo trabalho nos dois. `bootstrap` afirma que a coleção nunca foi agrupada e é recusado se já foi; `refresh` encaixa as faces sem pessoa nas pessoas que já existem e preserva os identificadores. Como o agrupamento automático pode ter chegado antes, `refresh` é o pedido seguro.
ScopeCollectionIdsstring[]até 50
Outras coleções que entram no mesmo escopo, para um evento partido em várias. Escopo maior é mais chance de sósia ao mesmo corte.
WebhookEndpointIdinteger
Para qual dos seus endpoints avisar quando terminar. Sem ele, o endpoint ativo da conta.

Na resposta

RunIdstring
O nome do run, o mesmo que vem no webhook e em `ListPeople`.
JobIdinteger
O que você acompanha em `GetJob` — o de um agrupamento que já estava a caminho, quando a resposta é ele.
Statusstring
Sempre `QUEUED`.
QueuedBehindinteger
Quantos runs estão na frente deste.
CrossCollectionScopeboolean
`true` quando o escopo cruza coleções — apagar a âncora apaga agrupamentos com faces das outras.
Requisição
{
  "CollectionId": "evento-2026",
  "Mode": "bootstrap",
  "ScopeCollectionIds": ["evento-2026-dia2"]
}
202
{
  "RunId": "run_918273",
  "JobId": 918273,
  "Status": "QUEUED",
  "QueuedBehind": 0,
  "CrossCollectionScope": true
}

Na fila.

POST/ X-Amz-Target: LumaVision.ListPeople

Lê as pessoas de uma coleção

Os grupos que o agrupamento formou, e o suficiente para lê-los honestamente: `Cohesion` (0 a 1) diz o quanto as faces do grupo concordam entre si; uma coesão baixa num grupo grande sinaliza uma mistura — é a única defesa contra um grupo ruim, já que você não corrige nada aqui; `UnassignedFaces` é quantas faces não entraram em grupo nenhum; `EffectiveThreshold` identifica o critério que produziu a resposta. Uma coleção nunca agrupada responde `People` vazio, sem `RunId` — nunca dispara um agrupamento implícito.

Na requisição

CollectionIdstringobrigatório
A coleção.
MaxResultsinteger0 a 5000; padrão 1000
Quantas pessoas por página. Acima do teto é recusado, não reduzido em silêncio.
MinPhotosintegerpadrão 1
Ignora grupos com menos fotos que isso.
NextTokenstring
O token da página anterior.
IncludeMembersbooleanpadrão false
Se cada pessoa vem com os rostos dela — a galeria pronta, sem uma segunda chamada por pessoa.
MembersPerPersonintegerpadrão 2000
Quantos rostos por pessoa quando `IncludeMembers` está ligado.

Na resposta

RunIdstring
O run que produziu estes grupos. Ausente numa coleção nunca agrupada.
ComputedAtdatetime
Quando o run terminou.
Scopeobject
`CollectionIds` e `FacesInScope`: o escopo que o run usou, não a coleção como está agora.
EffectiveThresholdobject
`BaseMedian`, `Note`: identificam o critério usado. Dois relatórios com critérios diferentes não são comparáveis.
TotalPeopleinteger
Quantos grupos existem, além desta página.
Returnedinteger
Quantos vieram nesta página.
Truncatedboolean
`true` quando há mais do que esta página.
UnassignedFacesinteger
Faces do escopo que não entraram em grupo nenhum — a medida honesta de cobertura.
AmbiguousFacesinteger
Das não atribuídas, as que dois grupos disputaram e nenhum venceu.
Peopleobject[]
Cada pessoa: `PersonId`, `Photos`, `Faces`, `Cohesion`, `FirstSeenAt`, `LastSeenAt`, `Representative` (FaceId, ImageId, ExternalImageId, BoundingBox), `AlsoIn`, e — quando pedido — `Members` com `MembersTruncated`. Cada membro traz `FaceId`, `ImageId`, `ExternalImageId`, `BoundingBox`, `Similarity` (o quanto a face concordava com a pessoa quando foi atribuída, na escala 0–100), `Position` e `PositionScore`.
CrossCollectionboolean
Se o serviço procurou a mesma pessoa nas suas outras coleções. `false` quer dizer que `AlsoIn` está ausente porque ninguém olhou.
NextTokenstring
Presente quando há mais páginas.
Requisição
{ "CollectionId": "evento-2026", "MaxResults": 100 }
200
{
  "RunId": "run_918273",
  "ComputedAt": "2026-08-07T03:19:42Z",
  "Scope": { "CollectionIds": ["evento-2026"], "FacesInScope": 4120 },
  "EffectiveThreshold": { "BaseMedian": 0.3872, "Note": "the search ruler, charged whole" },
  "TotalPeople": 412,
  "Returned": 1,
  "Truncated": true,
  "UnassignedFaces": 37,
  "AmbiguousFaces": 4,
  "People": [
    {
      "PersonId": "t7:p000001",
      "Photos": 28,
      "Faces": 28,
      "Cohesion": 0.91,
      "FirstSeenAt": "2026-08-01T09:12:00Z",
      "LastSeenAt": "2026-08-01T17:40:00Z",
      "Representative": { "FaceId": "019fe870-467d-7d49-a639-a94ef3db3915", "ImageId": "019fe870-467d-7432-9904-1fc838cf68a4", "ExternalImageId": "IMG_2291", "BoundingBox": { "Top": 0.1611, "Left": 0.2956, "Width": 0.4177, "Height": 0.4762 } },
      "AlsoIn": []
    }
  ],
  "CrossCollection": true,
  "NextToken": "eyJwaG90b3MiOiAyOCwgImlkIjogInQ3OnAwMDAwMDEifQ"
}

Os grupos.

POST/ X-Amz-Target: LumaVision.GetPerson

Um identificador que você anotou, respondido

A ação para a qual o banco do seu cliente aponta. Três respostas, as três 200: `OK`, o id ainda é ele mesmo; `SUPERSEDED`, o grupo foi fundido em outro e `ResolvesTo` traz o sucessor; `GONE`, o grupo foi apagado a pedido e `ErasedAt` diz quando. `ResourceNotFoundException` significa uma coisa só — um id que este serviço nunca emitiu — e nunca “apagamos”: dizer que o id está errado quando nós o apagamos manda o cliente caçar um bug no próprio banco.

Na requisição

CollectionIdstringobrigatório
Uma coleção sua. O id nomeia a pessoa, não onde ela foi encontrada.
PersonIdstringobrigatório
O identificador que você anotou, exatamente como foi emitido.

Na resposta

PersonIdstring
O id perguntado.
Statusstring
`OK`, `SUPERSEDED` ou `GONE`.
ResolvesTostring
O id que responde hoje — o próprio, ou o sucessor.
Personobject
A pessoa, na mesma forma da listagem.
ErasedAtdatetime
Só em `GONE`: quando foi apagada.
Requisição
{ "CollectionId": "evento-2026", "PersonId": "t7:p000001" }
200
{
  "PersonId": "t7:p000001",
  "Status": "SUPERSEDED",
  "ResolvesTo": "t7:p000044",
  "Person": { "PersonId": "t7:p000044", "Photos": 41, "Faces": 41, "Cohesion": 0.88, "AlsoIn": [] }
}

Foi fundido em outro grupo: guarde o novo identificador.

200
{
  "PersonId": "t7:p000001",
  "Status": "GONE",
  "ErasedAt": "2026-08-05T18:03:11Z"
}

Foi apagado a pedido.

POST/ X-Amz-Target: LumaVision.AnalyzeLiveness

Sinais de prova de vida por quadro

Para o cadastro por selfie em que você precisa se defender de alguém apontando a câmera para uma foto impressa ou para outra tela. Mande alguns quadros da câmera; volta um objeto por quadro, na ordem. Não decide nada — a lógica de sessão (peça para virar, agora peça para piscar) é sua — e não identifica ninguém. Um sinal olha os quadros juntos: `IdentityConsistency` diz se o rosto é o mesmo em todos, para pegar uma sequência montada com fotos de pessoas diferentes. Cada quadro passa pelos mesmos tetos de upload de qualquer imagem; a chamada conta um quadro por unidade.

Na requisição

FramesB64string[]obrigatório1 a 16
Os quadros, em base64. Um lote curto, não um vídeo.

Na resposta

Framesobject[]
Um por quadro: `FaceFound`, `BoundingBox`, `DetScore`, `PadScore` (probabilidade de ataque de apresentação), `VerifierScore`, `Pose`, `Quality`, `Iod` (distância entre os olhos em pixels), `SkinRgb`. Um quadro sem rosto vem com `FaceFound: false` e todo sinal ausente.
Metaobject
O que o pipeline diz de si — `PadAvailable`, `Detector` (rótulo público do modelo) — e `IdentityConsistency`: `MinSimilarity`, a menor semelhança entre dois quadros com rosto, na mesma escala 0–100 da busca, e `Frames`, quantos quadros entraram na conta. Ausente quando menos de dois quadros tinham rosto.
Requisição
{ "FramesB64": ["/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q==", "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q==", "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHR8fHiAcHh4gJC4nICIsIx4eKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q=="] }
200
{
  "Frames": [
    { "FaceFound": true, "BoundingBox": { "Top": 0.1611, "Left": 0.2956, "Width": 0.4177, "Height": 0.4762 }, "DetScore": 0.94, "PadScore": 0.03, "Iod": 41.2, "SkinRgb": [182, 141, 121] },
    { "FaceFound": true, "BoundingBox": { "Top": 0.1611, "Left": 0.2956, "Width": 0.4177, "Height": 0.4762 }, "DetScore": 0.93, "PadScore": 0.04, "Iod": 40.8, "SkinRgb": [180, 140, 120] },
    { "FaceFound": false }
  ],
  "Meta": { "PadAvailable": true, "Detector": "lumavision-v3", "IdentityConsistency": { "MinSimilarity": 99.62, "Frames": 2 } }
}

Um objeto por quadro.

POST/ X-Amz-Target: LumaVision.CreateWebhookEndpoint

Registra o endereço que recebe os resultados

Para onde `IndexFaces` e o agrupamento entregam. Normalmente uma vez, na configuração da integração. O `Secret` é devolvido uma única vez — guarde-o agora; ele não aparece em nenhuma outra ação, nem no log de chamadas, onde é redigido pelo nome. Registrar um endereço novo desativa o anterior, e a resposta diz qual.

Na requisição

Urlstringobrigatório
O endereço, público e HTTPS. Passa pela allowlist de saída da sua conta; fora dela é `InvalidParameterException`.

Na resposta

EndpointIdinteger
O identificador do endpoint — o que `IndexFaces` e `Cluster` aceitam em `WebhookEndpointId`.
Urlstring
O endereço, como veio.
Secretstring
A chave do HMAC das entregas. Só aqui, só agora.
ReplacedEndpointsobject[]
Os endpoints que estavam ativos e foram desativados por este registro.
Requisição
{ "Url": "https://api.seu-estudio.com.br/lumavision/webhook" }
200
{
  "EndpointId": 12,
  "Url": "https://api.seu-estudio.com.br/lumavision/webhook",
  "Secret": "whsec_3f8b21c0d9e44a17b7104e2a55cf49031c0d9e44a17b710",
  "ReplacedEndpoints": [{ "EndpointId": 11, "Url": "https://old.seu-estudio.com.br/hook" }]
}

Registrado. É a única resposta que carrega o segredo.

POST/ X-Amz-Target: LumaVision.ListWebhookEndpoints

Os endereços registrados

O que está registrado para esta conta, ativo ou não. O segredo não volta aqui — nem em lugar nenhum depois do registro. Uma conta sem endpoint ativo tem os resultados descartados, então esta é a primeira coisa a conferir quando um webhook não chega.

Na requisição

Na resposta

Endpointsobject[]
Cada um com `EndpointId`, `Url` e `Active`.
Requisição
{}
200
{
  "Endpoints": [
    { "EndpointId": 12, "Url": "https://api.seu-estudio.com.br/lumavision/webhook", "Active": true }
  ]
}

A lista.

POST/ X-Amz-Target: LumaVision.DeleteWebhookEndpoint

Desativa um endereço

Para as entregas para um endpoint. Ele é marcado como inativo, não removido, e desativar um já inativo responde igual — um retry precisa responder como a primeira chamada. Nenhum dado pessoal é apagado aqui, só uma configuração de entrega é desligada.

Na requisição

EndpointIdintegerobrigatório
O identificador devolvido no registro.

Na resposta

EndpointIdinteger
O que foi desativado.
Activeboolean
Sempre `false`.
Requisição
{ "EndpointId": 12 }
200
{ "EndpointId": 12, "Active": false }

Desativado.

POST/ X-Amz-Target: LumaVision.GetUsage

O número da fatura

O agregado do que a sua conta consumiu numa janela, por ação. `Calls` é quantas requisições, as recusadas inclusive — é a contagem de auditoria. `Units` é quanto trabalho foi entregue, e é o número da fatura: uma exclusão em lote com 800 ids é 1 chamada e 800 unidades. Só uma chamada 2xx tem unidades; uma recusa (4xx, 429, 5xx) e a repetição idempotente de um `IndexFaces` valem zero. Não conta como chamada: ler a própria fatura nunca é cobrado. Sem janela, os últimos 30 dias.

Na requisição

Startdatetime
Início da janela, inclusivo. Sem fuso, é UTC.
Enddatetime
Fim da janela, exclusivo.

Na resposta

Startdatetime
A janela efetivamente usada.
Enddatetime
Idem.
TotalCallsinteger
Chamadas na janela.
TotalUnitsinteger
Unidades na janela — o que é faturado.
TotalDurationMsinteger
Tempo de processamento consumido, somado.
ByActionobject[]
Por ação: `Action`, `Calls`, `Units`, `Errors`, `DurationMs`. Da mais chamada para a menos.
Requisição
{ "Start": "2026-08-01T00:00:00Z", "End": "2026-09-01T00:00:00Z" }
200
{
  "Start": "2026-08-01T00:00:00Z",
  "End": "2026-09-01T00:00:00Z",
  "TotalCalls": 128441,
  "TotalUnits": 402062,
  "TotalDurationMs": 91883412,
  "ByAction": [
    { "Action": "IndexFaces", "Calls": 112003, "Units": 111959, "Errors": 44, "DurationMs": 71220118 },
    { "Action": "SearchFacesByImage", "Calls": 16426, "Units": 16414, "Errors": 12, "DurationMs": 20624180 }
  ]
}

O consumo.

POST/ X-Amz-Target: LumaVision.ListCalls

O log de chamadas

“O que a nossa integração mandou às 3 da manhã de terça?” e “por que estamos vendo erro desde o deploy?”. Mais recentes primeiro, paginado por cursor. `ActedByPlatform` marca as chamadas que NÓS fizemos na sua conta, por suporte, e não a sua integração. Não conta como chamada.

Na requisição

Startdatetime
Início da janela.
Enddatetime
Fim da janela.
Actionstring
Só as chamadas dessa ação, como ela aparece no extrato (`IndexFaces`).
StatusCodeinteger
Só as chamadas que responderam esse código.
MaxResultsinteger0 a 4096; padrão 1000
Quantas por página.
NextTokenstring
O token da página anterior.

Na resposta

Callsobject[]
Cada chamada: `CallId`, `RequestId` (o `x-amzn-RequestId` que a resposta levou), `Action`, `StatusCode`, `DurationMs`, `Units`, `ActedByPlatform`, `CreatedAt`.
NextTokenstring
Presente quando há mais páginas.
Requisição
{ "Action": "SearchFacesByImage", "StatusCode": 400, "MaxResults": 50 }
200
{
  "Calls": [
    {
      "CallId": 99128374,
      "RequestId": "7b442ba9d518f0c1a4e3b7701c9e33fd",
      "Action": "SearchFacesByImage",
      "StatusCode": 400,
      "DurationMs": 388,
      "Units": 0,
      "ActedByPlatform": false,
      "CreatedAt": "2026-08-07T03:12:44.881Z"
    }
  ]
}

Uma página do extrato.

POST/ X-Amz-Target: LumaVision.GetCall

Uma chamada, inteira

A depuração de verdade: o corpo que foi enviado, o corpo que foi respondido, e os webhooks que aquela chamada causou. O corpo é guardado redigido — imagens viram `{"_image_bytes": n}`, vetores viram `{"_embedding_dims": n}`, segredos viram `[redacted]` — então o serviço registra que uma imagem passou, nunca a imagem. Não conta como chamada.

Na requisição

CallIdintegerobrigatório
O `CallId` que veio no extrato.

Na resposta

CallIdinteger
O identificador.
RequestIdstring
O `x-amzn-RequestId` da resposta.
Actionstring
A ação.
StatusCodeinteger
O código respondido.
DurationMsnumber
Quanto levou.
Unitsinteger
Quanto foi faturado: o trabalho entregue, zero numa recusa ou numa repetição idempotente.
ActedByPlatformboolean
`true` quando a chamada foi nossa, por suporte.
CreatedAtdatetime
Quando.
Methodstring
O verbo HTTP.
Pathstring
O caminho — `/` numa ação.
Requestobject
O corpo enviado, redigido.
Responseobject
O corpo respondido, redigido.
Webhooksobject[]
As entregas que esta chamada causou: `DeliveryId`, `Event`, `Status`, `Attempts`, `LastError`, `DeliveredAt`, `CreatedAt`. Vazio numa chamada síncrona.
Requisição
{ "CallId": 99128374 }
200
{
  "CallId": 99128374,
  "RequestId": "7b442ba9d518f0c1a4e3b7701c9e33fd",
  "Action": "IndexFaces",
  "StatusCode": 202,
  "DurationMs": 412,
  "Units": 1,
  "ActedByPlatform": false,
  "CreatedAt": "2026-08-07T03:12:44.881Z",
  "Method": "POST",
  "Path": "/",
  "Request": { "CollectionId": "evento-2026", "ExternalImageId": "IMG_2291", "Image": { "Bytes": { "_image_bytes": 1843221 } } },
  "Response": { "FaceRecords": [], "FaceModelVersion": "lumavision-v3", "ImageId": "019fe870-467d-7432-9904-1fc838cf68a4", "RequestId": 88213 },
  "Webhooks": [
    { "DeliveryId": 5512, "Event": "IndexFaces.Completed", "Status": "DELIVERED", "Attempts": 1, "DeliveredAt": "2026-08-07T03:19:02.441Z", "CreatedAt": "2026-08-07T03:18:58.010Z" }
  ]
}

A chamada, com o corpo redigido.

Operação

Tudo o que um tenant faz é uma ação (`POST /` com `X-Amz-Target`, acima). Sobra uma rota fora desse fio, e é de propósito: a sonda de saúde não pede chave, porque é o que você consulta quando a chave é justamente o que está em dúvida.

GET/health

O serviço está de pé

Sem versão e sem chave — é a única coisa aqui que você precisa poder perguntar quando a chave é justamente o que está em dúvida. Sempre 200; leia o corpo. `vector_delta_rows` é quantos vetores recém-indexados ainda não entraram no índice: perto de zero é o normal. Não conta como chamada.

200
{ "status": "ok", "db": true, "vector_delta_rows": 0 }

De pé.

200
{ "status": "degraded", "db": false }

Banco fora do ar: as rotas autenticadas vão responder 503.

Crie a conta, a chave sai na tela.

As primeiras 10.000 requisições por mês são de graça, sem cartão.