O perfil CUSTOM permite definir manualmente quais etapas de verificação serão executadas e em qual ordem. Use quando os perfis pré-definidos não atendem ao seu caso de uso.
Defina as etapas desejadas no array customSteps. Elas serão executadas na ordem informada.
const tx = await client.transactions.create({
purpose: 'DOCUMENT_SIGNATURE',
policy: {
profile: 'CUSTOM',
customSteps: ['BIOMETRIC_LIVENESS', 'BIOMETRIC_MATCH', 'OTP_CHALLENGE', 'OTP_VERIFY'],
},
signer: {
name: 'João Silva',
email: 'joao@example.com',
userExternalId: 'user-001',
},
document: { content: pdfBase64, filename: 'contrato.pdf' },
});
tx = client.transactions.create(CreateTransactionRequest(
purpose='DOCUMENT_SIGNATURE',
policy=Policy(
profile='CUSTOM',
custom_steps=['BIOMETRIC_LIVENESS', 'BIOMETRIC_MATCH', 'OTP_CHALLENGE', 'OTP_VERIFY'],
),
signer=Signer(name='João Silva', email='joao@example.com', user_external_id='user-001'),
document=InlineDocument(content=pdf_base64, filename='contrato.pdf'),
))
tx, _ := client.Transactions.Create(ctx, &signdocs.CreateTransactionRequest{
Purpose: signdocs.TransactionPurposeDocumentSignature,
Policy: signdocs.Policy{
Profile: signdocs.PolicyProfileCustom,
CustomSteps: []signdocs.StepType{
signdocs.StepTypeBiometricLive,
signdocs.StepTypeBiometricMatch,
signdocs.StepTypeOTPChallenge,
signdocs.StepTypeOTPVerify,
},
},
Signer: signdocs.Signer{Name: "João Silva", Email: "joao@example.com", UserExternalID: "user-001"},
Document: &signdocs.DocumentInline{Content: pdfBase64, Filename: "contrato.pdf"},
})
CreateTransactionRequest request = new CreateTransactionRequest();
request.purpose = "DOCUMENT_SIGNATURE";
request.policy = new Policy("CUSTOM",
List.of("BIOMETRIC_LIVENESS", "BIOMETRIC_MATCH", "OTP_CHALLENGE", "OTP_VERIFY"));
request.signer = new Signer("João Silva", "joao@example.com", "user-001");
request.document = new CreateTransactionRequest.InlineDocument(pdfBase64, "contrato.pdf");
Transaction tx = client.transactions().create(request);
$tx = $client->transactions->create(new CreateTransactionRequest(
purpose: 'DOCUMENT_SIGNATURE',
policy: new Policy(
profile: 'CUSTOM',
customSteps: ['BIOMETRIC_LIVENESS', 'BIOMETRIC_MATCH', 'OTP_CHALLENGE', 'OTP_VERIFY'],
),
signer: new Signer(name: 'João Silva', email: 'joao@example.com', userExternalId: 'user-001'),
document: ['content' => $pdfBase64, 'filename' => 'contrato.pdf'],
));
As etapas são geradas na ordem definida em customSteps. Execute cada uma seguindo os padrões documentados nos guias específicos:
| Tipo de Etapa | Guia de Referência |
|---|---|
CLICK_ACCEPT |
Assinatura Simples |
OTP_CHALLENGE / OTP_VERIFY |
Assinatura com OTP |
BIOMETRIC_LIVENESS / BIOMETRIC_MATCH |
Verificação Biométrica |
DIGITAL_SIGN_A1 |
Certificado Digital A1 |
const steps = await client.steps.list(tx.transactionId);
for (const step of steps) {
switch (step.type) {
case 'BIOMETRIC_LIVENESS': {
const start = await client.steps.start(tx.transactionId, step.stepId, {
captureMode: 'HOSTED_PAGE',
});
await client.steps.complete(tx.transactionId, step.stepId, {
livenessSessionId: start.livenessSessionId,
});
break;
}
case 'BIOMETRIC_MATCH':
await client.steps.complete(tx.transactionId, step.stepId, {
referenceImage: { source: 'BASE64_IMAGE', data: imageBase64 },
});
break;
case 'OTP_CHALLENGE':
await client.steps.start(tx.transactionId, step.stepId);
break;
case 'OTP_VERIFY':
await client.steps.complete(tx.transactionId, step.stepId, { code: otpCode });
break;
case 'CLICK_ACCEPT':
await client.steps.start(tx.transactionId, step.stepId);
await client.steps.complete(tx.transactionId, step.stepId, { accepted: true });
break;
}
}
steps = client.steps.list(tx.transaction_id)
for step in steps:
if step.type == 'BIOMETRIC_LIVENESS':
start = client.steps.start(tx.transaction_id, step.step_id,
StartStepRequest(capture_mode='HOSTED_PAGE'))
client.steps.complete(tx.transaction_id, step.step_id,
CompleteLivenessRequest(liveness_session_id=start.liveness_session_id))
elif step.type == 'BIOMETRIC_MATCH':
client.steps.complete(tx.transaction_id, step.step_id,
CompleteBiometricMatchRequest(reference_image=ReferenceImage(data=image_base64)))
elif step.type == 'OTP_CHALLENGE':
client.steps.start(tx.transaction_id, step.step_id)
elif step.type == 'OTP_VERIFY':
client.steps.complete(tx.transaction_id, step.step_id,
CompleteOtpRequest(code=otp_code))
elif step.type == 'CLICK_ACCEPT':
client.steps.start(tx.transaction_id, step.step_id)
client.steps.complete(tx.transaction_id, step.step_id,
CompleteClickRequest(accepted=True))
steps, _ := client.Steps.List(ctx, tx.TransactionID)
for _, step := range steps {
switch step.Type {
case signdocs.StepTypeBiometricLive:
start, _ := client.Steps.Start(ctx, tx.TransactionID, step.StepID,
&signdocs.StartStepRequest{CaptureMode: signdocs.CaptureModeHostedPage})
client.Steps.Complete(ctx, tx.TransactionID, step.StepID,
&signdocs.CompleteLivenessRequest{LivenessSessionID: start.LivenessSessionID})
case signdocs.StepTypeBiometricMatch:
client.Steps.Complete(ctx, tx.TransactionID, step.StepID,
&signdocs.CompleteBiometricMatchRequest{
ReferenceImage: &signdocs.ReferenceImage{Source: "BASE64_IMAGE", Data: imageBase64},
})
case signdocs.StepTypeOTPChallenge:
client.Steps.Start(ctx, tx.TransactionID, step.StepID, nil)
case signdocs.StepTypeOTPVerify:
client.Steps.Complete(ctx, tx.TransactionID, step.StepID,
&signdocs.CompleteOTPRequest{Code: otpCode})
case signdocs.StepTypeClickAccept:
client.Steps.Start(ctx, tx.TransactionID, step.StepID, nil)
client.Steps.Complete(ctx, tx.TransactionID, step.StepID,
&signdocs.CompleteClickRequest{Accepted: true})
}
}
List<Step> steps = client.steps().list(tx.transactionId);
for (Step step : steps) {
switch (step.type) {
case "BIOMETRIC_LIVENESS" -> {
var start = client.steps().start(tx.transactionId, step.stepId,
new StartStepRequest("HOSTED_PAGE"));
client.steps().complete(tx.transactionId, step.stepId,
Map.of("livenessSessionId", start.livenessSessionId));
}
case "BIOMETRIC_MATCH" ->
client.steps().complete(tx.transactionId, step.stepId,
Map.of("referenceImage", Map.of("source", "BASE64_IMAGE", "data", imageBase64)));
case "OTP_CHALLENGE" ->
client.steps().start(tx.transactionId, step.stepId);
case "OTP_VERIFY" ->
client.steps().complete(tx.transactionId, step.stepId, Map.of("code", otpCode));
case "CLICK_ACCEPT" -> {
client.steps().start(tx.transactionId, step.stepId);
client.steps().complete(tx.transactionId, step.stepId, Map.of("accepted", true));
}
}
}
$steps = $client->steps->list($tx->transactionId);
foreach ($steps as $step) {
match ($step->type) {
'BIOMETRIC_LIVENESS' => (function () use ($client, $tx, $step) {
$start = $client->steps->start($tx->transactionId, $step->stepId,
new StartStepRequest(captureMode: 'HOSTED_PAGE'));
$client->steps->complete($tx->transactionId, $step->stepId,
['livenessSessionId' => $start->livenessSessionId]);
})(),
'BIOMETRIC_MATCH' => $client->steps->complete($tx->transactionId, $step->stepId,
['referenceImage' => ['source' => 'BASE64_IMAGE', 'data' => $imageBase64]]),
'OTP_CHALLENGE' => $client->steps->start($tx->transactionId, $step->stepId),
'OTP_VERIFY' => $client->steps->complete($tx->transactionId, $step->stepId,
['code' => $otpCode]),
'CLICK_ACCEPT' => (function () use ($client, $tx, $step) {
$client->steps->start($tx->transactionId, $step->stepId);
$client->steps->complete($tx->transactionId, $step->stepId, ['accepted' => true]);
})(),
};
}
await client.transactions.finalize(tx.transactionId);
client.transactions.finalize(tx.transaction_id)
client.Transactions.Finalize(ctx, tx.TransactionID)
client.transactions().finalize(tx.transactionId);
$client->transactions->finalize($tx->transactionId);
Nota: Nem todas as combinações de etapas são válidas. Consulte a documentação da API para restrições de combinação. Por exemplo,
OTP_VERIFYdeve ser precedido porOTP_CHALLENGE.