Operaciones de Credenciales
Endpoints para emitir y revocar credenciales verificables. Todos soportan flujo prepare/submit y todos requieren una API key (X-ACTA-Key). Emitir Credencial (POST /contracts/vc/issue) y Emisión por Lote (POST /contracts/vc/batch-issue) requieren además que owner coincida con la wallet vinculada a tu API key (las keys con rol admin están exentas).
Emitir Credencial
POST /contracts/vc/issue
Emite una VC: almacena el payload en la bóveda del owner y escribe el estado de emisión = valid. Requiere API key.
Headers:
X-ACTA-Key: tu_api_key_aquiCuerpo de solicitud (Prepare):
{
"owner": "G...",
"vcId": "credential-123",
"vcData": "{\"@context\":[\"https://www.w3.org/ns/credentials/v2\",\"https://www.w3.org/ns/credentials/examples/v2\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":{\"id\":\"did:stellar:...\",\"name\":\"John Doe\"}}",
"issuer": "G...",
"issuerDid": "did:stellar:...",
"userSalt": "00...00",
"sourcePublicKey": "G..."
}El holder de la credencial se identifica con credentialSubject.id (un DID) dentro de vcData. No existe un campo holder aparte.
Cuerpo de solicitud (Submit):
{
"signedXdr": "AAAA..."
}Respuesta (Prepare):
{
"xdr": "AAAA...",
"network": "Test SDF Network ; September 2015"
}Respuesta (Submit):
{
"tx_id": "abc123..."
}Ejemplo:
# Prepare
curl -X POST https://api.testnet.acta.build/contracts/vc/issue \
-H "X-ACTA-Key: tu_key" \
-H "Content-Type: application/json" \
-d '{
"owner": "G...",
"vcId": "credential-123",
"vcData": "{\"@context\":[\"https://www.w3.org/ns/credentials/v2\",\"https://www.w3.org/ns/credentials/examples/v2\"],\"type\":[\"VerifiableCredential\"]}",
"issuer": "G...",
"issuerDid": "did:stellar:...",
"sourcePublicKey": "G..."
}'
# Submit (tras firmar)
curl -X POST https://api.testnet.acta.build/contracts/vc/issue \
-H "X-ACTA-Key: tu_key" \
-H "Content-Type: application/json" \
-d '{
"signedXdr": "AAAA..."
}'Emisión por Lote
POST /contracts/vc/batch-issue
Emite varias VC en la bóveda del mismo owner en una sola transacción. Cada entrada lleva solo vcId y vcData; el holder de cada credencial es credentialSubject.id dentro de su propio vcData (no hay campo holder). Requiere API key.
Límites: de 1 a 5 credenciales por lote (MAX_BATCH_SIZE = 5); vcId hasta 64 caracteres; vcData hasta 10,000 caracteres. Excederlos devuelve 400 batch_too_large, 400 vcs[i].vcId_too_long o 400 vcs[i].vcData_too_long.
Cuerpo de solicitud (Prepare):
{
"owner": "G...",
"issuer": "G...",
"issuerDid": "did:stellar:...",
"userSalt": "00...00",
"sourcePublicKey": "G...",
"vcs": [
{
"vcId": "credential-1",
"vcData": "{\"@context\":[\"https://www.w3.org/ns/credentials/v2\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":{\"id\":\"did:stellar:...\"}}"
},
{
"vcId": "credential-2",
"vcData": "{\"@context\":[\"https://www.w3.org/ns/credentials/v2\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":{\"id\":\"did:stellar:...\"}}"
}
]
}Cuerpo de solicitud (Submit):
{
"signedXdr": "AAAA..."
}Respuestas: Prepare devuelve { xdr, network }; Submit devuelve { tx_id }.
Revocar Credencial
POST /contracts/vc/revoke
Revoca una VC por ID en la bóveda de un owner concreto. Requiere una API key. La transacción debe firmarla el propietario de la bóveda (el contrato exige owner.require_auth()).
Cuerpo de solicitud (Prepare):
{
"owner": "G...",
"vcId": "credential-123",
"date": "2024-01-15T00:00:00.000Z",
"userSalt": "00...00",
"sourcePublicKey": "G..."
}Cuerpo de solicitud (Submit):
{
"signedXdr": "AAAA..."
}Respuesta (Prepare):
{
"xdr": "AAAA...",
"network": "Test SDF Network ; September 2015"
}Respuesta (Submit):
{
"tx_id": "abc123..."
}Requisito de DID del emisor
El issuerDid debe ser un `did:stellar` registrado y resoluble. Las direcciones de wallet "a secas" y los valores did:pkh ya no se aceptan. La API exige una vinculación controlador-DID: el controlador on-chain del DID debe ser igual al issuer que firma. Si difieren, la solicitud falla con:
{
"error": "issuerDid_controller_mismatch",
"message": "El controlador del DID del emisor no coincide con el emisor que firma"
}Comisiones
La emisión cobra una comisión on-chain calculada por la bóveda mediante quote_fee del factory y pagada por el emisor (mainnet: 1 USDC por credencial; testnet: 5 XLM). La API no acepta un override de comisión: hay una única comisión estándar más una comisión personalizada opcional por emisor, ambas resueltas on-chain.
Cuerpo de solicitud
Emitir Credencial
- owner (requerido): Dirección del propietario de la bóveda (G...)
- vcId (requerido): Identificador de credencial (máx 64 caracteres)
- vcData (requerido): Payload de datos de la credencial (string JSON, máx 10,000 caracteres). Debe incluir
@contextcon al menos"https://www.w3.org/ns/credentials/v2"ycredentialSubject.id(el DID del holder). La API cifravcData(AES-256-GCM) antes de almacenarlo on-chain - issuer (requerido): Dirección del emisor (G...)
- issuerDid (requerido):
did:stellarregistrado y resoluble cuyo controlador on-chain es igual aissuer - userSalt (opcional): salt de 32 bytes que selecciona la bóveda del owner; por defecto 32 bytes en cero
- sourcePublicKey (requerido): Fuente de transacción que firmará (debe ser el emisor)
Emisión por Lote
- owner (requerido): Dirección del propietario de la bóveda (G...)
- issuer (requerido): Dirección del emisor (G...)
- issuerDid (requerido):
did:stellarregistrado y resoluble cuyo controlador on-chain es igual aissuer - vcs (requerido): Array de
{ vcId, vcData }. CadavcDatalleva su propio DID de holder encredentialSubject.id - userSalt (opcional): salt de 32 bytes que selecciona la bóveda del owner
- sourcePublicKey (requerido): Fuente de transacción que firmará (debe ser el emisor)
Revocar Credencial
- owner (requerido): Dirección del propietario (G...) cuya bóveda contiene la credencial
- vcId (requerido): Identificador de credencial
- date (opcional): Timestamp ISO-8601 (por defecto: ahora)
- userSalt (opcional): salt de 32 bytes que selecciona la bóveda del owner
- sourcePublicKey (requerido): Fuente de transacción que firmará (debe ser el owner de la VC o admin del contrato)
Flujo Prepare/Submit
- Prepare: Envía solicitud con parámetros de operación (sin
signedXdr) - Firmar: Firma el
xdrdevuelto con tu wallet Stellar usando elnetworkpassphrase - Submit: Envía solicitud con
signedXdrpara ejecutar
Nota: issue almacena la credencial en la bóveda y la marca como válida en una sola transacción.