Guia de desenvolvimento
Este guia mostra os passos necessários para o desenvolvimento das integrações com o iD. A comunicação entre os sistemas emissores, verificadores e carteiras de usuários está representada na figura abaixo.

Plataforma iD e sistemas externos
Toda comunicação que envolve a troca de credenciais ocorre através de um protocolo padronizado DIDComm. Este protocolo usa WebSockets para estabelecer um canal bidirecional entre as partes. A implementação desse protocolo é realizada por componentes de software chamados Agentes. Todo agente possui uma carteira, que é uma base de dados onde são armazenadas as chaves privadas usadas para criptografar as mensagens. As credenciais de usuário também são armazenadas na carteira do agente do portador. Os agentes utilizam APIs para estabelecer conexões entre si e realizar transações sobre as credenciais. Para utilizar os serviços dos agentes do iD, é necessário estar cadastrado no portal iD Empresas (ver Primeiros Passos) e possuir um token de acesso.
Ambientes
Staging
O ambiente de staging possui os seguintes endereços:
Portal iD Empresas: https://staging-id.cpqd.com.br/empresas/
Documentação das APIs: https://staging-id.cpqd.com.br/docs/swagger/
URL base de API: https://staging-id.cpqd.com.br/api/
Endpoints
As APIs são organizadas conforme o tipo de agente: emissor, verificador e portador. Para cada um, temos dois endpoints: o agente e o serviço de gestão AFA (advanced function agent).
Hub do Emissor |
AFA |
https://staging-id.cpqd.com.br/api/hub-issuer/afa |
Agente |
https://staging-id.cpqd.com.br/api/hub-issuer/agent |
|
Hub do Verificador |
AFA |
https://staging-id.cpqd.com.br/api/hub-verifier/afa |
Agente |
https://staging-id.cpqd.com.br/api/hub-verifier/agent |
|
Hub do Portador |
AFA |
https://staging-id.cpqd.com.br/api/hub-holder/afa |
Agente |
https://staging-id.cpqd.com.br/api/hub-holder/agent |
Para maiores detalhes consulte a documentação detalhada no swagger.
Obter o token de acesso do agente V1
Para utilizar as APIs do agente é necessário obter um token de acesso. Utilize o Wallet Name, disponível no menu “Minha carteira” no portal iD Empresas , e, caso já possua, a API Key previamente fornecida para realizar requisições ao serviço AFA.
Passo 1. Para obter o token de acesso para o agente, use a chamada ao serviço AFA:
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-issuer/afa/v2/wallet/$WALLET_NAME/token' \
--header 'ApiKey: $API_KEY'
- Onde:
WALLET_NAME: nome da carteira
API_KEY: chave de autorização da carteira
A requisição possui corpo vazio. Em caso de sucesso, irá retornar o token de acesso no formato JSON conforme abaixo:
{
"token": "<TOKEN>"
}
Endpoints V2
As APIs V2 são organizadas conforme o tipo de agente: emissor e verificador. Cada agente possui seu endpoint, e o emissor também possui o serviço de autenticação (Auth Manager).
Obter o token de acesso do agente V2
Para utilizar a API do agente emissor V2 é necessário obter um token de acesso. Para isso, é necessário utilizar os campos Wallet Name e Secret do menu “Minha carteira” no portal iD Empresas .
Passo 1. Para obter o token de acesso para o agente, use a chamada ao serviço Auth Manager:
curl -X POST 'https://staging-id.cpqd.com.br/api/auth-manager/v1/token' \
--data '{
"wallet_name": "$WALLET_NAME",
"password": "$SECRET"
}'
- Onde:
WALLET_NAME: nome da carteira
SECRET: senha da chave de autorização da carteira
Em caso de sucesso, irá retornar o token de acesso no formato JSON conforme abaixo:
{
"token": "<TOKEN>"
}
Gerenciamento de conexões
Antes de executar uma transação com credenciais, é necessário estabelecer uma conexão entre o agente do emissor ou verificador, e o agente do portador.
Para isso, deve-se criar um convite no formato de URL e compartilhá-lo com os usuários. Esse link pode ser transformado em um QR Code usando ferramentas como o QR Code Generator para facilitar a leitura por câmeras e celulares.
Note
Caso o QR Code fique muito ‘denso’, você pode usar uma ferramenta como a URL Shortener para diminuir o tamanho da URL.
Gerar convite de conexão out-of-band
Uma das formas de criar um convite de conexão é a API /out-of-band. Para isso, use a seguinte chamada de API.
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/out-of-band/create-invitation?multi_use=$MULTI_USE' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"alias": "alias for the connection",
"my_label": "label for the connection invitation",
"metadata": {},
"handshake_protocols": [
"https://didcomm.org/connections/1.0"
]
}'
- Opcionalmente, preencha os campos:
MULTI_USE: (opcional) se igual a “true”, indica que o convite pode ser utilizado várias vezes, abrindo novas conexões a cada leitura. Caso contrário, ele perde a validade após a primeira leitura (valor padrão = false)
alias: (opcional) rótulo arbitrário para a conexão
my_label: (opcional) rótulo arbitrário para o convite
metadata: (opcional) objeto que será anexado à conexão, utilizado para transportar informações de contexto associado ao convite. A informação não é transferida para o outro lado da conexão
A resposta é semelhante ao exemplo abaixo:
{
"state":"initial",
"trace":false,
"invi_msg_id":"fa1bb934-17c9-4507-81f3-0e94d33422ec",
"oob_id":"a1e2bc29-662c-4699-b30c-67d3bfa64e37",
"invitation":{
"@type":"https://didcomm.org/out-of-band/1.1/invitation",
"@id":"fa1bb934-17c9-4507-81f3-0e94d33422ec",
"label":"label for the connection invitation",
"handshake_protocols":[
"https://didcomm.org/connections/1.0"
],
"services":[
{
"id":"#inline",
"type":"did-communication",
"recipientKeys":[
"did:key:z6MkqkhAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST#z6Mkqk hAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST"
],
"serviceEndpoint":"https://staging-id.cpqd.com.br/api/hub-issuer/ agent-didcomm"
}
]
},
"invitation_url":"https://staging-id.cpqd.com.br/api/hub-issuer/agent-didcomm?oob=eyJAdHlwZSI6ICJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzEuMS9pbnZpdGF0aW9uIiwgIkBpZCI6ICJmYTFiYjkzNC0xN2M5LTQ1MDctODFmMy0wZTk0ZDMzNDIyZWMiLCAibGFiZWwiOiAiY3BxZC1pc3N1ZXIiLCAiaGFuZHNoYWtlX3Byb3RvY29scyI6IFsiaHR0cHM6Ly9kaWRjb21tLm9yZy9jb25uZWN0aW9ucy8xLjAiXSwgInNlcnZpY2VzIjogW3siaWQiOiAiI2lubGluZSIsICJ0eXBlIjogImRpZC1jb21tdW5pY2F0aW9uIiwgInJlY2lwaWVudEtleXMiOiBbImRpZDprZXk6ejZNa3FraEFKZk10R0NpcEppaEVoZE1UckpGclM3emF3UUFIdFJKdEZ1UHpubVNUI3o2TWtxa2hBSmZNdEdDaXBKaWhFaGRNVHJKRnJTN3phd1FBSHRSSnRGdVB6bm1TVCJdLCAic2VydmljZUVuZHBvaW50IjogImh0dHBzOi8vc3RhZ2luZy1pZC5jcHFkLmNvbS5ici9hcGkvaHViLWlzc3Vlci9hZ2VudC1kaWRjb21tIn1dfQ==",
"alias":"alias for the connection"
}
- Observe os campos na resposta:
invi_msg_id: contém o identificador da mensagem do convite
invitation: objeto do convite, que deve ser compartilhado com o usuário
invitation_url: URL do convite, que contém o objeto do convite codificado em base64
Quando o usuário aceitar o convite e a conexão for estabelecida, o webhook do agente receberá uma mensagem no tópico /connections contendo os dados da conexão.
Descartar convite de conexão
Para descartar um convite de conexão é necessário usar o invi_msg_id na seguinte chamada de API:
curl -X DELETE 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/out-of-band/invitations/$INVI_MSG_ID' \
--header 'Authorization: Bearer $TOKEN'
A resposta não contém corpo, mesmo em caso de sucesso.
Consultar conexões
Para verificar as conexões existentes do agente, use a seguinte chamada de API:
curl -X GET 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/connections' \
--header 'Authorization: Bearer $TOKEN'
- Os seguintes parâmetros da URL podem ser utilizados para filtrar a busca, de forma opcional:
alias: valor arbitrário definido no convite
state: estado da conexão (valores possíveis: abandoned, active, completed, error, init, invitation, request, response, start)
A resposta é semelhante ao exemplo abaixo:
{
"results":[
{
"created_at":"2023-06-02T12:32:56.936708Z",
"updated_at":"2023-06-02T12:32:57.525851Z",
"invitation_msg_id":"lp8e5610-0799-443d-90b2-6656dbcd89f8",
"connection_id":"7i8e5880-4e47-4be2-9d2b-561b06e8ff23",
"their_did":"EzqgxguBfLpXBz9eUkY4l7",
"their_role":"inviter",
"their_label":"issuer",
"connection_protocol":"connections/1.0",
"my_did":"2iMyPY5xcNxHzcR5HtLWd4",
"state":"active",
"rfc23_state":"completed",
"request_id":"633335wc-9426-4217-bf21-43c12b5y67f3",
"accept":"auto",
"invitation_mode":"once",
"invitation_key":"97jLje2Ca6T4HadXwhVif4TmeESXpWYcNqoWPPRX8N1o",
"routing_state":"none"
}
]
}
- Observe os campos:
connection_id: identificador da conexão
status: status da conexão
their_label: nome do agente conectado
Note
Para realizar uma transação com um agente, a conexão deve possuir status “active”. Qualquer outro status indica que ela não está pronta para uso.
Remover conexões
Para remover uma conexão é necessário usar o connection_id na seguinte chamada de API:
curl -X DELETE 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/connections/$CONNECTION_ID' \
--header 'Authorization: Bearer $TOKEN'
A resposta não contém corpo, mesmo em caso de sucesso.
Aceitar convite de conexão
Para iniciar uma conexão com um agente emissor ou verificador, o usuário precisa receber um convite de conexão. Esse convite pode ser lido de uma URL ou um QR Code, pelo aplicativo da carteira do usuário. A URL contém todas as informações necessárias para estabelecer a conexão.
Passo 1. Faça a leitura da URL do convite. Observe o parâmetro c_i ou oob e faça um decode em base64 do seu valor. Isso irá gerar o objeto com os dados do convite. Exemplo de URL:
https://staging-id.cpqd.com.br/api/hub-issuer/agent-didcomm?oob=eyJAdHlwZSI6ICJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzEuMS9pbnZpdGF0aW9uIiwgIkBpZCI6ICJmYTFiYjkzNC0xN2M5LTQ1MDctODFmMy0wZTk0ZDMzNDIyZWMiLCAibGFiZWwiOiAiY3BxZC1pc3N1ZXIiLCAiaGFuZHNoYWtlX3Byb3RvY29scyI6IFsiaHR0cHM6Ly9kaWRjb21tLm9yZy9jb25uZWN0aW9ucy8xLjAiXSwgInNlcnZpY2VzIjogW3siaWQiOiAiI2lubGluZSIsICJ0eXBlIjogImRpZC1jb21tdW5pY2F0aW9uIiwgInJlY2lwaWVudEtleXMiOiBbImRpZDprZXk6ejZNa3FraEFKZk10R0NpcEppaEVoZE1UckpGclM3emF3UUFIdFJKdEZ1UHpubVNUI3o2TWtxa2hBSmZNdEdDaXBKaWhFaGRNVHJKRnJTN3phd1FBSHRSSnRGdVB6bm1TVCJdLCAic2VydmljZUVuZHBvaW50IjogImh0dHBzOi8vc3RhZ2luZy1pZC5jcHFkLmNvbS5ici9hcGkvaHViLWlzc3Vlci9hZ2VudC1kaWRjb21tIn1dfQ==
Objeto decodificado por base64:
{"@type":"https://didcomm.org/out-of-band/1.1/invitation","@id":"fa1bb934-17c9-4507-81f3-0e94d33422ec","label":"cpqd-issuer","handshake_protocols":["https://didcomm.org/connections/1.0"],"services":[{"id":"#inline","type":"did-communication","recipientKeys":["did:key:z6MkqkhAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST#z6MkqkhAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST"],"serviceEndpoint":"https://staging-id.cpqd.com.br/api/hub-issuer/agent-didcomm"}]}
Observe o campo @type, ele indica o tipo de conexão que deve ser feita.
Passo 2. Se o tipo de conexão for didcomm.org/out-of-band/1.1/invitation, utilize a API out-of-band para aceitar o convite e iniciar a conexão. Use o objeto do convite no corpo da requisição.
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-holder/agent/out-of-band/receive-invitation' \
--header 'ApiKey: $API_KEY' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"@type":"https://didcomm.org/out-of-band/1.1/invitation",
"@id":"fa1bb934-17c9-4507-81f3-0e94d33422ec",
"label":"cpqd-issuer",
"handshake_protocols":[
"https://didcomm.org/connections/1.0"
],
"services":[
{
"id":"#inline",
"type":"did-communication",
"recipientKeys":[
"did:key:z6MkqkhAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST#z6MkqkhAJfMtGCipJihEhdMTrJFrS7zawQAHtRJtFuPznmST"
],
"serviceEndpoint":"https://staging-id.cpqd.com.br/api/hub-issuer/agent-didcomm"
}
]
}'
A resposta é semelhante ao exemplo abaixo:
{
"state":"deleted",
"created_at":"2024-05-13T13:46:48.822907Z",
"updated_at":"2024-05-13T13:46:48.822907Z",
"trace":false,
"oob_id":"1668f2d5-e001-400d-84eb-000e00a84d1a",
"invi_msg_id":"fa1bb934-17c9-4507-81f3-0e94d33422ec",
"invitation":{ },
"connection_id":"f5ed4759-1904-42c3-8384-941ea11ebd25",
"role":"receiver",
"multi_use":false
}
Observe na resposta o campo connection_id. Ele indica o identificador da conexão estabelecida entre os agentes e é necessário para chamadas de API que dependem da conexão.
Desenvolvimento de webhooks
Um webhook é um serviço que funciona como um listener dos agentes do iD, recebendo notificações sobre eventos de uma transação em andamento. Ele deve ter um endereço público (URL) e opcionalmente, uma API Key para autenticação de acesso. Essas informações devem ser registradas no portal iD Empresas , associado à sua carteira, para que os agentes possam enviar notificações de eventos e status das transações relacionadas a credenciais (ver Configuração de Webhooks).
O agente enviará requisições HTTP REST para os webhooks cadastrados, adicionando o caminho /topic/<topic> na URL, conforme o tipo de transação. Se definido uma API-Key para autenticação, o agente enviará o header X-Api-Key na requisição.
Note
A implementação do webhook deve responder às mensagens recebidas com o status HTTP 200 OK o mais rápido possível, para evitar o bloqueio desnecessário de recursos no agente. Atrasos na resposta do webhook causam bloqueios na transação e degradação no desempenho do agente, prejudicando outros usuários. Se ocorrer um erro no webhook que resulte em uma resposta com status HTTP 500, a transação em execução no agente será interrompida.
A seguir, estão descritos os tópicos mais relevantes que devem ser implementados pelo webhook, quando aplicável.
POST /topic/connections
Este é o tópico para os eventos de conexão entre agentes. As mensagens recebidas neste tópico possuem um conteúdo semelhante ao exemplo abaixo:
{
"state":"active",
"created_at":"2024-05-29T13:16:43.740444Z",
"updated_at":"2024-05-29T13:16:44.012376Z",
"connection_id":"26da8131-5f2a-4f08-a439-606a34e97be7",
"my_did":"B7E1svNn3rjEQ4wZoKx4Se",
"their_did":"7TVNrbFdijB9RWMHYTFJE1",
"their_label":"bob",
"their_role":"invitee",
"connection_protocol":"connections/1.0",
"rfc23_state":"completed",
"invitation_key":"7BjFYbGJuh6B5FTZnMkxiKnbPvnmAaYEEPcha4PgDBe2",
"routing_state":"none",
"accept":"auto",
"invitation_mode":"once"
}
- Observe os campos:
connection_id: identificador da conexão
status: status da conexão
their_label: rótulo do convite identificando o agente
invitation_msg_id: identificador do convite
Utilize o valor de connection_id nas requisições que exigem conexão ativa entre agentes, para emissão e prova de credenciais.
O campo status pode ter os seguintes valores:
State |
Descrição |
---|---|
invitation |
Convite de conexão criado. Indica que um agente criou um convite de conexão para outro agente, gerando o identificador da conexão (connection_id), o conteúdo do convite e a URL do convite (invitation_url). |
request |
Pedido de conexão enviado. Indica que uma resposta a um convite de conexão foi enviado pelo portador ao autor do convite. |
response |
Resposta de conexão enviada. Indica que uma resposta ao pedido de conexão foi enviada pelo autor do convite ao portador. |
active |
Conexão ativa. Indica que o convite foi aceito e que a conexão está ativa para troca de mensagens. |
completed |
Indica que a conexão é válida e está pronta para uso. |
A figura a seguir ilustra o fluxo de eventos quando um usuário aceita um convite de conexão, pelos webhooks dos agentes portador e emissor.

Figura 1: Eventos do fluxo de conexão.
POST /topic/out-of-band
O protocolo out-of-band gera mensagens para este tópico. Através dele, o webhook de quem criou o convite recebe o identificador da conexão criada quando o convite for aceito pelo outro agente. Simultaneamente, o tópico /topic/connections também é acionado quando a conexão é criada.
As mensagens recebidas neste tópico são semelhantes ao exemplo abaixo:
{
"state":"await-response",
"created_at":"2024-05-13T17:10:18.252500Z",
"updated_at":"2024-05-13T17:10:18.252500Z",
"trace":false,
"oob_id":"08f75e7b-a3ef-4cfc-a2d8-58b8ac43d2ad",
"invi_msg_id":"b77631a4-6138-4b7d-812f-a67a1bc655db",
"invitation":{
"@type":"https://didcomm.org/out-of-band/1.1/invitation",
"@id":"b77631a4-6138-4b7d-812f-a67a1bc655db",
"label":"alice",
"handshake_protocols":[
"https://didcomm.org/connections/1.0"
],
"services":[
{
"id":"#inline",
"type":"did-communication",
"recipientKeys":[
"did:key:z6Mkei8GLd4kf7gieKmcJrWWGZGmvFju6seJvZ2ZNphuN3Uc#z6Mkei8GLd4kf7gieKmcJrWWGZGmvFju6seJvZ2ZNphuN3Uc"
],
"serviceEndpoint":"http://alice-agent:9002"
}
]
},
"connection_id":"4853b392-3f3a-4e03-baa5-9f9841550d37",
"our_recipient_key":"FsDkNpKKaCFXpvudHYfRTin6gU3gzPxEY7dYYjtSphE",
"role":"sender",
"multi_use":false
}
Observe o campo connection_id, ele contém o identificador da conexão que foi criada.
POST /topic/basicmessages
Este é o tópico para os eventos de mensagens genéricas de texto, que podem ser trocados entre a aplicação e o usuário, usando a conexão ativa entre os agentes. As mensagens recebidas neste tópico possuem um conteúdo semelhante ao exemplo abaixo:
{
"@id": "123456780",
"@type": "https://didcomm.org/basicmessage/1.0/message",
"~l10n": { "locale": "en" },
"sent_time": "2019-01-15 18:42:01Z",
"content": "This is a sample message."
}
POST /topic/issue_credential_v2_0
Este é o tópico para os eventos de emissão de credenciais. As mensagens recebidas neste tópico possuem um conteúdo semelhante ao exemplo abaixo:
{
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"role":"issuer",
"initiator":"self",
"auto_offer":true,
"auto_remove":false,
"thread_id":"5661591f-f715-4787-adf4-1ec2eeb77271",
"state":"offer-sent",
"created_at":"2024-05-13T18:05:27.111632Z",
"updated_at":"2024-05-13T18:05:27.111632Z",
"cred_ex_id":"bcea07be-6d07-461c-946f-a11d3e7b9013"
}
- Observe os campos:
connection_id: identificador da conexão
state: estado da transação
cred_ex_id: identificador da transação
Os dados da oferta ou pedido de credencial podem ser acessados pelo identificador da transação cred_ex_id na API /issue-credentials-2.0.
O campo state pode ter os seguintes valores:
State |
Descrição |
---|---|
proposal_sent |
Proposta de emissão de credencial enviada. Indica que uma proposta para emissão de credencial foi enviada pelo portador ao emissor. Isso pode ocorrer porque o portador iniciou o fluxo ou nos casos em que ele deseja realizar ajustes nos dados da credencial ofertada (após status offer_received). |
proposal_received |
Proposta de emissão de credencial recebida. Indica que a proposta enviada pelo portador para emissão de uma credencial foi recebida pelo emissor. Isso pode ocorrer porque o portador iniciou o fluxo enviando uma proposta ou nos casos em que o portador deseja realizar ajustes nos dados da credencial ofertada (após status offer_received do portador). |
offer_sent |
Oferta de emissão enviada. Indica que uma oferta para emissão de credencial foi enviada pelo emissor ao portador. Isso pode acontecer porque o emissor iniciou o fluxo ou após o recebimento de uma proposta de emissão (após status proposal_received do emissor). |
offer_received |
Oferta de emissão recebida pelo usuário. Indica que a oferta para emissão de credencial enviada pelo emissor foi recebida pelo portador. Isso pode acontecer porque o emissor iniciou o fluxo enviando uma oferta ou após o recebimento da proposta de emissão enviada pelo portador (após status proposal_sent do portador). |
request_sent |
Requisição de emissão enviada. Indica que uma requisição de emissão de credencial foi enviada pelo holder ao emissor. Isso significa que a oferta recebida pelo portador (offer_received) foi aceita. |
request_received |
Requisição de emissão recebida. Indica que a requisição de emissão da credencial referente ao aceite da oferta pelo portador foi recebida pelo emissor. No ID, após o aceite da oferta pelo portador, o fluxo passa automaticamente por esse status. |
credential_issued |
Credencial emitida. Indica que a credencial foi emitida e enviada pelo emissor ao portador. |
credential_received |
Credencial recebida. Indica que a credencial emitida foi recebida e seus dados foram armazenados na carteira digital do portador. |
credential_acked |
Credencial reconhecida. Indica que o fluxo de emissão de credencial foi concluído com sucesso e a credencial foi reconhecida como emitida e armazenada na carteira do holder. |
abandoned |
Credencial rejeitada. Ocorre após recusa de proposta pelo emissor ou de oferta pelo portador. |
A figura a seguir ilustra o fluxo de eventos nos webhook dos agentes portador e emissor, quando um emissor envia uma oferta para um usuário.

Figura 2: Eventos do fluxo de emissão de credencial.
O diagrama abaixo representa os estados do fluxo de emissão que são enviados para os webhooks, conforme o evento.

Figura 3: Máquina de estados do fluxo de emissão de credencial.
Adicionalmente, o evento de revogação possui um conteúdo semelhando ao exemplo abaixo. Este evento tambémé enviado para /topic/issuer_cred_rev, conforme descrito na sequencia.
{
"state":"credential_revoked",
"created_at":"2024-03-08T11:54:32.763787Z",
"updated_at":"2024-03-08T12:17:43.033893Z",
"trace":true,
"credential_exchange_id":"f4519117-d58f-4886-b4d0-4e9d621ab1fc",
"connection_id":"4b375ed9-b9c4-4ecf-804d-d7a76dd2a88e",
"thread_id":"5828692b-db00-483a-803c-db3636bfa942",
"initiator":"self",
"role":"issuer",
"credential_definition_id":"UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person",
"schema_id":"UeaHZWGeYCx4wy96VE9LQo:2:person:1.0",
"credential_proposal_dict":{ },
"credential_offer_dict":{ },
"credential_offer":{ },
"credential_request":{ },
"credential":{
"schema_id":"UeaHZWGeYCx4wy96VE9LQo:2:person:1.0",
"cred_def_id":"UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person",
"rev_reg_id":"UeaHZWGeYCx4wy96VE9LQo:4:UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person:CL_ACCUM:007946f3-a3ed-4a68-be43-c142b7aa58d0"
},
"auto_offer":false,
"auto_issue":true,
"auto_remove":false,
"revoc_reg_id":"UeaHZWGeYCx4wy96VE9LQo:4:UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person:CL_ACCUM:007946f3-a3ed-4a68-be43-c142b7aa58d0",
"revocation_id":"1"
}
POST /topic/issuer_cred_rev
Este é o tópico para os eventos de revogação de credenciais. As mensagens recebidas neste tópico possuem um conteúdo semelhante ao exemplo abaixo:
{
"state": "revoked",
"created_at": "2024-03-08T12:01:51.541629Z",
"updated_at": "2024-03-08T12:17:42.959648Z",
"record_id": "b07ba2b5-f1df-4f2d-9e12-ea1718150130",
"cred_ex_id": "f4519117-d58f-4886-b4d0-4e9d621ab1fc",
"rev_reg_id": "UeaHZWGeYCx4wy96VE9LQo:4:UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person:CL_ACCUM:007946f3-a3ed-4a68-be43-c142b7aa58d0",
"cred_def_id": "UeaHZWGeYCx4wy96VE9LQo:3:CL:990:person",
"cred_rev_id": "1",
"cred_ex_version": "1"
}
- Observe os campos:
state: estado da transação
cred_ex_id: identificador da transação
POST /topic/present_proof_v2_0
Este é o tópico para os eventos de prova de credenciais. As mensagens recebidas neste tópico possuem um conteúdo semelhante ao exemplo abaixo:
{
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"role":"verifier",
"initiator":"self",
"auto_present":false,
"auto_verify":true,
"thread_id":"c9634376-705e-4abe-a149-a39061a1a7cf",
"state":"request-sent",
"trace":false,
"created_at":"2024-05-13T18:35:04.810535Z",
"updated_at":"2024-05-13T18:35:04.810535Z",
"pres_ex_id":"b64f0bf8-410d-486a-aff6-5cee86ca4e82"
}
- Observe os campos:
connection_id: identificador da conexão
state: estado da transação
pres_ex_id: identificador da transação, necessário para recuperar os dados do pedido/compartilhamento
O campo state pode ter os seguintes valores:
State |
Descrição |
---|---|
request_sent |
Pedido de prova enviado. Indica que um pedido de prova contendo os valores que precisam ser compartilhados e os predicados que precisam ser cumpridos foi enviado pelo verificador ao portador. |
request_received |
Pedido de prova recebido. Indica que o pedido de prova enviado pelo verificador contendo os valores que precisam ser compartilhados e os predicados que precisam ser cumpridos foi recebido pelo portador. |
proposal_sent |
Proposta de prova enviada. Indica que uma mensagem opcional foi enviada pelo portador para iniciar um processo de apresentação de prova junto a um verificador, ou após status request_received, quando o portador deseja enviar uma resposta propondo um formato de apresentação diferente. |
proposal_received |
Proposta de prova recebida. Indica que a mensagem opcional enviada pelo portador para iniciar um processo de apresentação de prova, ou após status request_received, quando o portador deseja enviar uma resposta propondo um formato de apresentação diferente, foi recebida pelo verificador. |
presentation_sent |
Apresentação de credencial enviada. Indica que o portador concordou com o pedido e uma resposta contendo as provas assinadas foi enviada ao verificador. |
presentation_received |
Apresentação de credencial recebida. Indica que a resposta do portador contendo as provas assinadas foi recebida pelo verificador. |
verified |
Credencial verificada. Indica que o processo de verificação foi concluído com o consentimento do portador. Nesse status, o verificador consegue acesso aos dados da prova. |
presentation_acked |
Confirmação do recebimento da apresentação. Indica que o verificador concluiu a execução da prova. |
abandoned |
Pedido ou proposta abandonada. Ocorre quando o verificador ou o usuário sinalizam que não desejam prosseguir com o compartilhamento de dados. |

Figura 4: Eventos do fluxo de pedido de prova de credencial.
O diagrama abaixo representa os estados do fluxo de prova que são enviados para os webhooks, conforme o evento.

Figura 5: Máquina de estados do fluxo de pedido de prova de credencial.
POST /topic/revocation-notify
Este tópico trata dos eventos de revogação de credencial para agentes que utilizaram uma credencial. Uma mensagem recebida neste tópico indica a revogação de uma credencial utilizada em uma prova anterior, com um conteúdo semelhante ao exemplo abaixo:
{
"message": "Motivo da revogação",
"proofs": [
"32a78638-f781-4ae6-929f-573a2513fcad",
"b213b8e3-065a-4d5f-976a-698bb3628dbd"
]
"cred_rev_uuid": "9230b99e-ab1f-4845-b1ae-e612a6dd0b7f",
"cred_def_id": "BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
- Observe os campos:
message: motivo da revogação
proofs: lista de identificadores de transações de prova, correspondentes aos pres_ex_ids das provas nas quais a credencial revogada foi utilizada. Esses identificadores podem ser usados para visualizar os dados da prova (ver Visualizar registro de prova)
cred_rev_uuid: identificador de revogação da credencial
cred_def_id: identificador da definição da credencial
Emissão de credenciais
Criar oferta de credencial
Para emitir uma credencial, o emissor deve enviar uma oferta para o usuário utilizando uma conexão existente entre os agentes. A oferta deve usar uma definição de credencial e preencher os atributos da credencial com os dados do usuário. O webhook do portador receberá os dados e os enviará para o usuário avaliar. O usuário poderá revisar os dados e aceitar a oferta, dando seu consentimento para a emissão. Após o aceite do usuário, a credencial será emitida automaticamente e o webhook do emissor receberá os eventos do fluxo.
Para criar uma oferta de credencial, use a seguinte chamada de API:
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/issue-credential-2.0/send-offer' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"auto_remove": true,
"comment": "a human readable comment about the offer",
"connection_id": "65796d3b-1e3b-4727-b1b8-8166cc777a90",
"credential_preview": {
"@type": "issue-credential/2.0/credential-preview",
"attributes": [
{
"name": "cred_attribute_1",
"value": "value_1"
},
{
"name": "cred_attribute_2",
"value": "value_2"
}
]
},
"filter": {
"indy": {
"cred_def_id": "BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
},
},
"trace": false
}'
- Preencha os campos adequadamente:
connection_id: identificador da conexão entre agentes
comment: (opcional) texto descritivo da oferta
cred_def_id: identificador da definição da credencial (ver cadastro da carteira)
attributes: lista dos atributos da oferta, contendo o nome e valor de cada atributo
Em caso de sucesso, a resposta será parecida com o exemplo abaixo:
{
"state":"offer-sent",
"created_at":"2024-05-13T18:05:27.111632Z",
"updated_at":"2024-05-13T18:05:27.111632Z",
"cred_ex_id":"bcea07be-6d07-461c-946f-a11d3e7b9013",
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"thread_id":"5661591f-f715-4787-adf4-1ec2eeb77271",
"initiator":"self",
"role":"issuer",
"cred_preview":{ },
"cred_proposal":{ },
"cred_offer":{ },
"by_format":{ },
"auto_offer":true,
"auto_remove":false
}
Observe o campo cred_ex_id que é o identificador da transação. Ele é importante para a execução do fluxo de emissão.
O webhook do portador receberá a oferta e deverá notificar o aplicativo do usuário via push. A oferta será apresentada ao usuário, que poderá aceitar, recusar ou gerar uma proposta.
Caso o usuário aceite a oferta, a credencial é emitida automaticamente e armazenada na carteira do portador. Se o usuário recusar a oferta, o evento recebido no webhook do emissor deve ser analisado, pois pode conter a causa da recusa. Uma nova oferta pode então ser gerada, se necessário.
Visualizar registro de emissão
Para visualizar os dados de uma credencial é preciso consultar o registro da transação de emissão da credencial através do identificador cred_ex_id. Use a seguinte chamada de API:
curl -X GET 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/issue-credential-2.0/records/$CRED_EXCH_ID' \
--header 'Authorization: Bearer $TOKEN'
Em caso de sucesso, a resposta será parecida com o exemplo abaixo:
{
"cred_ex_record":{
"created_at":"2024-05-13T18:05:27.111632Z",
"updated_at":"2024-05-13T18:05:27.111632Z",
"cred_ex_id":"bcea07be-6d07-461c-946f-a11d3e7b9013",
"state":"offer-sent",
"cred_preview":{
"@type":"https://didcomm.org/issue-credential/2.0/credential-preview",
"attributes":[
{
"name":"cred_attribute_1",
"value":"value_1"
},
{
"name":"cred_attribute_2",
"value":"value_2"
}
]
},
"cred_proposal":{
"@type":"https://didcomm.org/issue-credential/2.0/propose-credential",
"@id":"3454e43e-5c6b-440f-81f4-aafdddcff54e",
"comment":"a human readable comment about the offer",
"credential_preview":{
"@type":"https://didcomm.org/issue-credential/2.0/credential-preview",
"attributes":[
{
"name":"cred_attribute_1",
"value":"value_1"
},
{
"name":"cred_attribute_2",
"value":"value_2"
}
]
},
"formats":[ ],
"filters~attach":[ ]
},
"cred_offer":{
"@type":"https://didcomm.org/issue-credential/2.0/offer-credential",
"@id":"5661591f-f715-4787-adf4-1ec2eeb77271",
"~thread":{ },
"comment":"comentario",
"credential_preview":{
"@type":"https://didcomm.org/issue-credential/2.0/credential-preview",
"attributes":[
{
"name":"cred_attribute_1",
"value":"value_1"
},
{
"name":"cred_attribute_2",
"value":"value_2"
}
]
},
"formats":[ ],
"offers~attach":[ ]
},
"by_format":{
"cred_proposal":{
"indy":{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
},
"cred_offer":{
"indy":{
"schema_id":"BNL8CmbhE9JSudsWcr1U8a:2:prefs:1.0",
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4",
"key_correctness_proof":{ },
"nonce":"1085462880236207347265349"
}
}
},
"auto_offer":true,
"auto_remove":false
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"thread_id":"5661591f-f715-4787-adf4-1ec2eeb77271"
},
"indy":{ }
}
- O objeto da resposta contém alguns campos de interesse:
cred_ex_record/cred_proposal/credential_preview: objeto da proposta da credencial
cred_ex_record/cred_offer/credential_preview: objeto da oferta feita pelo usuário
cred_ex_record/state: status da emissão da credencial
Criar oferta a partir de proposta de credencial
O emissor pode receber uma proposta de credencial gerada por um usuário. As propostas são recebidas no webhook do emissor, mas também é possível consultar através da chamada de API:
curl -X GET 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/issue-credential-2.0/records?state=proposal-received' \
--header 'Authorization: Bearer $TOKEN'
- Observe os campos da resposta:
cred_exch_id: identificador da transação de emissão
cred_proposal/credential_preview: objeto da proposta da credencial
O emissor deve avaliar os valores da proposta e gerar uma oferta com ajustes, se necessário. Para gerar a oferta para a proposta recebida, use o identificador cred_exch_id na chamada de API:
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/issue-credential-2.0/records/$CRED_EXCH_ID/send-offer' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"counter_preview":{
"@type":"issue-credential/2.0/credential-preview",
"attributes":[
{
"name":"cred_attribute_1",
"value":"value_1"
},
{
"name":"cred_attribute_2",
"value":"value_2"
}
]
},
"filter":{
"indy":{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
}
}'
- Preencha os campos adequadamente:
attributes: lista dos atributos da proposta, contendo o nome e valor de cada atributo
cred_def_id: (opcional) identificador da definição da credencial
Revogar uma credencial
Uma credencial emitida para um usuário pode ser revogada pelo emissor quando, por alguma razão, ela perde a sua validade. Nem toda credencial é revogável, isso é uma característica definida na definição da credencial.
A revogação pode ser realizada diretamente no portal iD Empresas (ver Revogação de credenciais) ou por API.
Para revogar a credencial, é preciso utilizar o identificador do registro da transação de emissão cred_ex_id e que a transação esteja no estado credential_acked. Caso exista uma conexão ativa com o agente do portador, é possível indicar o connection_id para notificar o usuário que a credencial foi revogada. Use a seguinte chamada de API:
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/revocation/revoke' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"comment": "the reason why the credential is being revoked",
"connection_id": "65796d3b-1e3b-4727-b1b8-8166cc777a90",
"cred_ex_id": "bcea07be-6d07-461c-946f-a11d3e7b9013",
"notify": true,
"notify_version": "v1_0",
"publish": true,
"thread_id": "5661591f-f715-4787-adf4-1ec2eeb77271"
}'
- Preencha adequadamente os campos:
cred_ex_id: identificador da transação de emissão
notify: (opcional) deve ser “true” para notificar o usuário que a credencial foi revogada
connection_id: (opcional) identificador da conexão com o agente portador. É obrigatório, se notify = true
thread_id: (opcional) identificador da thread da transação de emissão. É obrigatório, se notify = true
Toda revogação é registrada e pode ser consultada usando a seguinte API:
curl -X GET 'https://staging-id.cpqd.com.br/api/hub-issuer/agent/v2/revocation/credential-record' \
--header 'Authorization: Bearer $TOKEN'
Prova de credenciais
Criar pedido de prova
Para receber os dados de uma credencial, o verificador cria um pedido de prova para o usuário, utilizando uma conexão existente entre os agentes. O verificador deve indicar quais atributos deseja receber e, opcionalmente, indicar uma definição de credencial que contenha os atributos desejados.
O webhook do portador irá receber o pedido de prova e enviar para o usuário. O usuário poderá aceitar o pedido, dando seu consentimento para compartilhar seus dados. O webhook do verificador irá receber os eventos do fluxo e o resultado da prova de validação, os dados compartilhados podem ser obtidos pela API de consulta do registro da transação.
Caso o usuário recuse o pedido ou ocorra um problema, o evento recebido no webhook do verificador deve ser analisado, pois pode conter a causa da recusa. Esse tipo de problema pode ocorrer quando o usuário não possui todas as credenciais solicitadas.
Para criar um pedido de prova, use a seguinte chamada de API:
curl -X POST 'https://staging-id.cpqd.com.br/api/hub-verifier/agent/present-proof-2.0/send-request' \
--header 'ApiKey: $API_KEY' \
--header 'Authorization: Bearer $TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"comment":"A test for unbounded proof request",
"presentation_request":{
"indy":{
"name":"Proof request for user documents",
"non_revoked":{
"from":1714561200,
"to":1714766775
},
"requested_attributes":{
"document_data":{
"names":["cpf", "rg"],
"restrictions":[
{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
]
},
"email_data":{
"name":"email",
"restrictions":[
{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
]
}
},
"requested_predicates":{ },
"nonce":"1",
"version":"1.0"
}
},
"auto_remove":false,
"auto_verify":true,
"trace":false
}'
- Preencha os campos adequadamente:
connection_id: identificador da conexão entre agentes
comment: (opcional) mensagem para o usuário descrevendo o pedido de prova
name: nome arbitrário para a transação
non_revoked: (opcional) período em que a credencial deve ser válida, ou não-revogada. O período é definido em formato epoch e deve ser preechido para que a validação considere o critério de não-revogado
requested_attributes: lista de nomes arbitrários contendo a relação dos campos das credenciais que devem ser compartilhados
cred_def_id: (opcional) identificador da definição da credencial
Em caso de sucesso, a resposta será parecida com o exemplo abaixo:
{
"state":"request-sent",
"created_at":"2024-05-13T18:35:04.810535Z",
"updated_at":"2024-05-13T18:35:04.810535Z",
"trace":false,
"pres_ex_id":"b64f0bf8-410d-486a-aff6-5cee86ca4e82",
"connection_id":"65796d3b-1e3b-4727-b1b8-8166cc777a90",
"thread_id":"c9634376-705e-4abe-a149-a39061a1a7cf",
"initiator":"self",
"role":"verifier",
"pres_request":{
"@type":"https://didcomm.org/present-proof/2.0/request-presentation",
"@id":"c9634376-705e-4abe-a149-a39061a1a7cf",
"comment":"A test for unbounded proof request",
"will_confirm":true,
"formats":[
{
"attach_id":"indy",
"format":"hlindy/proof-req@v2.0"
}
],
"request_presentations~attach":[
{
"@id":"indy",
"mime-type":"application/json",
"data":{
"base64":"eyJyZXF1ZXN0ZWRf...xLjAifQ=="
}
}
]
},
"by_format":{
"pres_request":{
"indy":{
"requested_attributes":{
"document_data":{
"names":["cpf", "rg"],
"restrictions":[
{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
]
},
"email_data":{
"name":"email",
"restrictions":[
{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
]
}
},
"requested_predicates":{ },
"name":"Proof request for user documents",
"nonce":"1",
"version":"1.0"
}
}
},
"auto_present":false,
"auto_verify":true,
"auto_remove":false
}
O webhook do portador receberá o pedido de prova e deverá notificar o aplicativo do usuário via push. O pedido será apresentado ao usuário, que poderá aceitar ou recusar.
Caso o usuário aceite o pedido, a credencial é compartilhada e verificada automaticamente. Se o usuário recusar, um evento de problema será enviado ao webhook do verificador.
O resultado da validação e os dados compartilhados podem ser consultados através do registro da transação, conforme item a seguir.
Visualizar registro de prova
Para visualizar dados de uma prova de credencial é preciso consultar o registro da transação. Para isso utilize o identificador pres_ex_id na seguinte chamada de API:
curl -X GET 'https://staging-id.cpqd.com.br/api/hub-verifier/agent/present-proof-2.0/records/$PRES_EXCH_ID' \
--header 'ApiKey: $API_KEY' \
--header 'Authorization: Bearer $TOKEN'
{
"state":"done",
"created_at":"2024-05-24T18:44:06.857648Z",
"updated_at":"2024-05-24T18:47:57.652950Z",
"trace":false,
"pres_ex_id":"91f7413e-6a79-45a5-8430-ea4e872e8aec",
"connection_id":"24a785f4-5b07-450c-9956-0c08c11d7f13",
"thread_id":"c086c90c-59eb-4317-ae0f-47645614edb1",
"initiator":"self",
"role":"verifier",
"pres_request":{ },
"pres":{ },
"by_format":{
"pres_request":{
"indy":{
"version":"1.0",
"requested_attributes":{
"document_data":{
"names":[ "rg", "cpf" ],
"restrictions":[
{
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4"
}
]
}
},
"requested_predicates":{ },
"name":"documentos",
"nonce":"572837955284685091800653"
}
},
"pres":{
"indy":{
"proof":{
"proofs":[ ],
"aggregated_proof":{ }
},
"requested_proof":{
"revealed_attrs":{ },
"revealed_attr_groups":{
"document_data":{
"sub_proof_index":0,
"values":{
"cpf":{
"raw":"value_2",
"encoded":"937164683293106646596997"
},
"rg":{
"raw":"value_1",
"encoded":"805530634343087287422"
}
}
}
},
"self_attested_attrs":{ },
"unrevealed_attrs":{
"email_data":{
"values":{
"email":{ }
}
}
},
"predicates":{ }
},
"identifiers":[
{
"schema_id":"WaSyJsvwSJVh245c9dYUwT:2:login:1.0",
"cred_def_id":"BNL8CmbhE9JSudsWcr1U8a:3:CL:447:default4",
"rev_reg_id":null,
"timestamp":null
}
]
}
}
},
"verified":"true",
"verified_msgs":[ "RMV_GLB_NRI" ],
"auto_present":false,
"auto_verify":true,
"auto_remove":false
}
- Observe os campos da resposta:
by_format/pres/indy/requested_proof/revealed_attr_groups: atributos compartilhados pelo usuário. Inspecione o campo ‘values’ para ler os dados do usuário
by_format/pres/indy/requested_proof/unrevealed_attrs: atributos solicitados e não compartilhados pelo usuário
verified: indica se os dados são válidos