CLPASSINATURASSuporte à integração ↗

GUIAS PARA INTEGRADORES

API de assinatura digital em C#, PHP e Node.js

Aprenda a enviar PDF para assinatura pela API CLP em C#, PHP e Node.js, com chave no servidor e idempotência.

Um envio lógico, uma chave persistida

Antes da chamada, grave no ERP um identificador único de até 128 caracteres, o PDF e os dados do envio. Use esse identificador em Idempotency-Key. Após timeout, repita com a mesma chave e os mesmos bytes e campos; gerar outra chave pode duplicar o envio.

A API aceita PDF de até 20 MB e 200 páginas, sem senha ou assinatura prévia, e um a cinco destinatários em paralelo. recipients é uma string contendo um array JSON. Deixe a biblioteca gerar o cabeçalho multipart com seu delimitador.

Configuração dos exemplos

Defina no ambiente do servidor CLP_API_KEY, CLP_OPERATION_ID, CLP_PDF_PATH, CLP_RECIPIENT_NAME e CLP_RECIPIENT_EMAIL. O identificador da operação não deve conter dados pessoais. A pessoa destinatária precisa autorizar o teste.

C# com HttpClient

Crie um console app .NET 8 ou superior e use o arquivo abaixo como Program.cs. O exemplo usa apenas bibliotecas da plataforma. Ao tentar novamente, reconstrua a requisição e reabra o arquivo; não reutilize um stream já consumido.

criar-envelope.cs
// .NET 8+. Console app. Executar envia convites reais; use destinatarios autorizados.
using System.Net.Http.Headers;
using System.Text.Json;

static string Required(string name) => Environment.GetEnvironmentVariable(name)
    is { Length: > 0 } value ? value : throw new InvalidOperationException($"Defina {name}");
var key = Required("CLP_API_KEY");
var operation = Required("CLP_OPERATION_ID"); // Persistido antes da primeira tentativa.
using var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })
    { Timeout = TimeSpan.FromSeconds(60) };
using var form = new MultipartFormDataContent();
var pdf = new StreamContent(File.OpenRead(Required("CLP_PDF_PATH")));
pdf.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
form.Add(pdf, "pdf", "contrato.pdf");
form.Add(new StringContent("Contrato da operacao " + operation), "title");
form.Add(new StringContent(operation), "external_id");
form.Add(new StringContent("platform"), "protection_mode");
form.Add(new StringContent(JsonSerializer.Serialize(new[] { new {
    name = Required("CLP_RECIPIENT_NAME"), email = Required("CLP_RECIPIENT_EMAIL")
} })), "recipients");
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.clpsistemas.com.br/v1/envelopes") { Content = form };
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);
request.Headers.Add("Idempotency-Key", operation);
using var response = await client.SendAsync(request);
if ((int)response.StatusCode is not (200 or 201))
    throw new HttpRequestException($"CLP respondeu HTTP {(int)response.StatusCode}");
using var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(JsonSerializer.Serialize(new {
    id = result.RootElement.GetProperty("id").GetGuid(),
    status = result.RootElement.GetProperty("status").GetString()
}));
// Persista o ID retornado. Timeout: reconstruir a requisicao com os mesmos dados e chave.

Baixar exemplo completo

PHP com cURL

Requer PHP 8.2 ou superior e extensão cURL. Execute no servidor com php criar-envelope.php. Não desabilite a verificação TLS. O exemplo não segue redirecionamentos com a credencial.

criar-envelope.php
<?php
// PHP 8.2+ com cURL. Executar envia convites reais; use destinatarios autorizados.
function requiredEnv(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '') throw new RuntimeException("Defina $name");
    return $value;
}
$key = requiredEnv('CLP_API_KEY');
$operation = requiredEnv('CLP_OPERATION_ID'); // Persistido no ERP antes da primeira tentativa.
$path = requiredEnv('CLP_PDF_PATH');
if (!is_file($path) || !is_readable($path)) throw new RuntimeException('PDF nao acessivel');
$curl = curl_init('https://api.clpsistemas.com.br/v1/envelopes');
curl_setopt_array($curl, [
    CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 60, CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $key", "Idempotency-Key: $operation"],
    CURLOPT_POSTFIELDS => [
        'pdf' => new CURLFile($path, 'application/pdf', 'contrato.pdf'),
        'title' => 'Contrato da operacao ' . $operation, 'external_id' => $operation,
        'protection_mode' => 'platform',
        'recipients' => json_encode([['name' => requiredEnv('CLP_RECIPIENT_NAME'),
            'email' => requiredEnv('CLP_RECIPIENT_EMAIL')]], JSON_THROW_ON_ERROR)
    ]
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$failed = $body === false;
curl_close($curl);
if ($failed) throw new RuntimeException('Falha de transporte; repetir com os mesmos dados e a mesma chave');
if (!in_array($status, [200, 201], true)) throw new RuntimeException("CLP respondeu HTTP $status");
$envelope = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
echo json_encode(['id' => $envelope['id'], 'status' => $envelope['status']], JSON_THROW_ON_ERROR);
// Persista envelope.id junto a operation; nao registre chave, PDF ou links privados.

Baixar exemplo completo

Node.js com fetch e FormData

Requer Node.js 22 ou superior. Execute com node criar-envelope.mjs. Nenhuma dependência externa é necessária. O limite de tempo protege o processo; um timeout não prova que a API deixou de criar o envelope.

criar-envelope.mjs
// Node.js 22+. Executar envia convites reais; use destinatarios autorizados.
import { readFile } from 'node:fs/promises';
function required(name) { const value = process.env[name]; if (!value) throw new Error(`Defina ${name}`); return value; }
const key = required('CLP_API_KEY');
const operation = required('CLP_OPERATION_ID'); // Persistido no ERP antes da primeira tentativa.
const form = new FormData();
form.set('pdf', new Blob([await readFile(required('CLP_PDF_PATH'))], { type: 'application/pdf' }), 'contrato.pdf');
form.set('title', 'Contrato da operacao ' + operation);
form.set('external_id', operation);
form.set('protection_mode', 'platform');
form.set('recipients', JSON.stringify([{ name: required('CLP_RECIPIENT_NAME'), email: required('CLP_RECIPIENT_EMAIL') }]));
const response = await fetch('https://api.clpsistemas.com.br/v1/envelopes', {
  method: 'POST', headers: { Authorization: `Bearer ${key}`, 'Idempotency-Key': operation },
  body: form, signal: AbortSignal.timeout(60000), redirect: 'error'
});
if (![200, 201].includes(response.status)) throw new Error(`CLP respondeu HTTP ${response.status}; consulte o codigo de erro sem registrar dados sensiveis.`);
const envelope = await response.json();
console.log(JSON.stringify({ id: envelope.id, status: envelope.status }));
// Persista envelope.id junto a operation. Timeout: mesmos bytes/dados e mesma chave.

Baixar exemplo completo

Entenda o retorno antes de atualizar o ERP

201 indica envelope criado e convites enfileirados. 200 pode indicar repetição idempotente. Grave o id retornado; nenhum dos dois significa assinatura concluída. A consulta GET /v1/envelopes/{id} retorna o objeto em envelope e os destinatários em recipients.

Em 409 idempotency_conflict, não repita em loop: compare os dados com a operação original. Em 401/403, revise autenticação e permissões. Em 429 ou erro transitório de infraestrutura, use espera progressiva com variação aleatória e limite de tentativas; cabeçalhos e corpo JSON não são garantidos em todas as falhas. Nunca registre a chave, PDF ou link privado em logs.

Depois do envio, implemente o receptor de eventos e a exportação do resultado.