Use cases
Contratos
Gere contratos profissionais com assinaturas eletrônicas e versionamento
Este guia mostra como gerar contratos legais profissionais usando RenderHub.
Visão Geral
Contratos são documentos legais que requerem atenção especial:
- ✅ Contratos de serviço
- ✅ Contratos de trabalho
- ✅ Termos de uso e políticas
- ✅ NDA (Non-Disclosure Agreement)
- ✅ Contratos de locação
Estrutura de Dados
interface Contract {
// Metadados
id: string;
number: string;
type: 'service' | 'employment' | 'nda' | 'lease' | 'other';
status: 'draft' | 'pending_signature' | 'signed' | 'expired' | 'terminated';
// Partes
parties: {
contractor: {
type: 'individual' | 'company';
name: string;
documentId: string; // CPF/CNPJ
address: string;
city: string;
state: string;
zipCode: string;
representative?: string; // Para empresas
};
contractee: {
type: 'individual' | 'company';
name: string;
documentId: string;
address: string;
city: string;
state: string;
zipCode: string;
representative?: string;
};
};
// Datas
startDate: Date;
endDate?: Date;
signedAt?: Date;
// Termos
terms: {
scope: string; // Escopo do trabalho/serviço
deliverables?: string[]; // Entregas
payment: {
amount: number;
currency: string;
schedule: 'monthly' | 'quarterly' | 'one-time' | 'milestone';
dueDay?: number;
};
termination?: string; // Condições de rescisão
confidentiality?: string; // Cláusula de confidencialidade
custom?: Array<{
title: string;
content: string;
}>;
};
// Assinaturas
signatures: {
contractor?: {
signedAt: Date;
signature: string; // Base64 ou URL da assinatura
ip: string;
};
contractee?: {
signedAt: Date;
signature: string;
ip: string;
};
};
// Controle de versão
version: number;
previousVersionId?: string;
}
Template HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@page {
margin: 2.5cm;
}
body {
font-family: 'Times New Roman', serif;
font-size: 12pt;
line-height: 1.6;
color: #000;
}
.header {
text-align: center;
margin-bottom: 40px;
border-bottom: 2px solid #000;
padding-bottom: 20px;
}
.contract-title {
font-size: 16pt;
font-weight: bold;
text-transform: uppercase;
margin-bottom: 10px;
}
.contract-number {
font-size: 10pt;
color: #666;
}
.parties {
margin-bottom: 30px;
}
.party {
margin-bottom: 20px;
}
.party-label {
font-weight: bold;
text-transform: uppercase;
margin-bottom: 5px;
}
.party-info {
margin-left: 20px;
line-height: 1.4;
}
.clause {
margin-bottom: 25px;
}
.clause-number {
font-weight: bold;
margin-bottom: 5px;
}
.clause-title {
font-weight: bold;
margin-bottom: 8px;
}
.clause-content {
text-align: justify;
margin-left: 20px;
}
.subcl ause {
margin: 10px 0 10px 40px;
}
.signatures {
margin-top: 60px;
page-break-inside: avoid;
}
.signature-block {
margin-top: 40px;
}
.signature-line {
border-top: 1px solid #000;
width: 300px;
margin: 60px 0 5px 0;
}
.signature-label {
font-size: 10pt;
}
.signature-image {
max-width: 200px;
max-height: 80px;
margin-bottom: 10px;
}
.signature-info {
font-size: 9pt;
color: #666;
margin-top: 5px;
}
.footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #ccc;
font-size: 9pt;
color: #666;
text-align: center;
}
.watermark {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-45deg);
font-size: 72pt;
color: rgba(255, 0, 0, 0.1);
z-index: -1;
}
</style>
</head>
<body>
{{#if (eq status "draft")}}
<div class="watermark">RASCUNHO</div>
{{/if}}
<div class="header">
<div class="contract-title">
{{typeLabel type}}
</div>
<div class="contract-number">Contrato Nº {{number}}</div>
</div>
<div class="parties">
<div class="party">
<div class="party-label">Contratante:</div>
<div class="party-info">
<strong>{{parties.contractor.name}}</strong>,
{{#if (eq parties.contractor.type "company")}}
empresa inscrita no CNPJ sob nº {{formatCNPJ parties.contractor.documentId}},
{{#if parties.contractor.representative}}
neste ato representada por {{parties.contractor.representative}},
{{/if}}
{{else}}
inscrito(a) no CPF sob nº {{formatCPF parties.contractor.documentId}},
{{/if}}
com endereço em {{parties.contractor.address}},
{{parties.contractor.city}} - {{parties.contractor.state}},
CEP {{formatZipCode parties.contractor.zipCode}}.
</div>
</div>
<div class="party">
<div class="party-label">Contratado:</div>
<div class="party-info">
<strong>{{parties.contractee.name}}</strong>,
{{#if (eq parties.contractee.type "company")}}
empresa inscrita no CNPJ sob nº {{formatCNPJ parties.contractee.documentId}},
{{#if parties.contractee.representative}}
neste ato representada por {{parties.contractee.representative}},
{{/if}}
{{else}}
inscrito(a) no CPF sob nº {{formatCPF parties.contractee.documentId}},
{{/if}}
com endereço em {{parties.contractee.address}},
{{parties.contractee.city}} - {{parties.contractee.state}},
CEP {{formatZipCode parties.contractee.zipCode}}.
</div>
</div>
</div>
<p style="margin-bottom: 30px; text-align: justify;">
As partes acima qualificadas têm, entre si, justo e acertado o presente
{{typeLabel type}}, que se regerá pelas cláusulas seguintes e pelas
condições descritas no presente.
</p>
<div class="clause">
<div class="clause-number">CLÁUSULA 1ª</div>
<div class="clause-title">DO OBJETO</div>
<div class="clause-content">
{{terms.scope}}
</div>
</div>
{{#if terms.deliverables}}
<div class="clause">
<div class="clause-number">CLÁUSULA 2ª</div>
<div class="clause-title">DAS ENTREGAS</div>
<div class="clause-content">
O CONTRATADO se compromete a realizar as seguintes entregas:
<ul style="margin-top: 10px;">
{{#each terms.deliverables}}
<li>{{this}}</li>
{{/each}}
</ul>
</div>
</div>
{{/if}}
<div class="clause">
<div class="clause-number">CLÁUSULA {{clauseNumber "payment"}}</div>
<div class="clause-title">DO PAGAMENTO</div>
<div class="clause-content">
Pelo serviço prestado, o CONTRATANTE pagará ao CONTRATADO o valor de
<strong>{{formatCurrency terms.payment.amount terms.payment.currency}}</strong>
{{#if (eq terms.payment.schedule "monthly")}}
mensalmente, todo dia {{terms.payment.dueDay}} de cada mês.
{{else if (eq terms.payment.schedule "one-time")}}
em pagamento único.
{{else if (eq terms.payment.schedule "milestone")}}
conforme atingimento de marcos do projeto.
{{/if}}
</div>
</div>
<div class="clause">
<div class="clause-number">CLÁUSULA {{clauseNumber "term"}}</div>
<div class="clause-title">DO PRAZO</div>
<div class="clause-content">
O presente contrato terá vigência de <strong>{{formatDate startDate}}</strong>
{{#if endDate}}
até <strong>{{formatDate endDate}}</strong>
{{else}}
por prazo indeterminado
{{/if}}.
</div>
</div>
{{#if terms.confidentiality}}
<div class="clause">
<div class="clause-number">CLÁUSULA {{clauseNumber "confidentiality"}}</div>
<div class="clause-title">DA CONFIDENCIALIDADE</div>
<div class="clause-content">
{{terms.confidentiality}}
</div>
</div>
{{/if}}
{{#if terms.termination}}
<div class="clause">
<div class="clause-number">CLÁUSULA {{clauseNumber "termination"}}</div>
<div class="clause-title">DA RESCISÃO</div>
<div class="clause-content">
{{terms.termination}}
</div>
</div>
{{/if}}
{{#if terms.custom}}
{{#each terms.custom}}
<div class="clause">
<div class="clause-number">CLÁUSULA {{clauseNumber @index}}</div>
<div class="clause-title">{{uppercase title}}</div>
<div class="clause-content">
{{content}}
</div>
</div>
{{/each}}
{{/if}}
<div class="clause">
<div class="clause-number">CLÁUSULA FINAL</div>
<div class="clause-title">DO FORO</div>
<div class="clause-content">
As partes elegem o foro da Comarca de {{parties.contractor.city}}/{{parties.contractor.state}}
para dirimir quaisquer dúvidas ou litígios oriundos do presente contrato,
com renúncia expressa a qualquer outro, por mais privilegiado que seja.
</div>
</div>
<p style="margin: 40px 0 20px 0; text-align: justify;">
E por estarem assim justos e contratados, firmam o presente instrumento, em duas vias de igual teor e forma.
</p>
<p style="text-align: right; margin-bottom: 60px;">
{{parties.contractor.city}}, {{formatDate (or signedAt startDate)}}
</p>
<div class="signatures">
<div class="signature-block">
{{#if signatures.contractor.signature}}
<img src="{{signatures.contractor.signature}}" class="signature-image">
{{/if}}
<div class="signature-line"></div>
<div class="signature-label">
<strong>{{parties.contractor.name}}</strong><br>
Contratante
{{#if signatures.contractor.documentId}}
<br>CPF/CNPJ: {{signatures.contractor.documentId}}
{{/if}}
</div>
{{#if signatures.contractor.signedAt}}
<div class="signature-info">
Assinado digitalmente em {{formatDateTime signatures.contractor.signedAt}}<br>
IP: {{signatures.contractor.ip}}
</div>
{{/if}}
</div>
<div class="signature-block">
{{#if signatures.contractee.signature}}
<img src="{{signatures.contractee.signature}}" class="signature-image">
{{/if}}
<div class="signature-line"></div>
<div class="signature-label">
<strong>{{parties.contractee.name}}</strong><br>
Contratado
{{#if signatures.contractee.documentId}}
<br>CPF/CNPJ: {{signatures.contractee.documentId}}
{{/if}}
</div>
{{#if signatures.contractee.signedAt}}
<div class="signature-info">
Assinado digitalmente em {{formatDateTime signatures.contractee.signedAt}}<br>
IP: {{signatures.contractee.ip}}
</div>
{{/if}}
</div>
</div>
<div class="footer">
Documento gerado eletronicamente em {{formatDateTime (now)}}<br>
Versão {{version}} | Hash: {{documentHash}}
</div>
</body>
</html>
Implementação
import { RenderHubClient } from '@renderhub/sdk';
import crypto from 'crypto';
export class ContractService {
private renderHub: RenderHubClient;
constructor() {
this.renderHub = new RenderHubClient({
apiKey: process.env.RENDERHUB_API_KEY!,
});
}
async generateContract(contract: Contract): Promise<Buffer> {
// 1. Calcular hash do documento
const documentHash = this.calculateHash(contract);
// 2. Preparar dados
const templateData = {
...contract,
documentHash,
startDate: contract.startDate.toISOString().split('T')[0],
endDate: contract.endDate?.toISOString().split('T')[0],
};
// 3. Gerar PDF
const pdf = await this.renderHub.generatePDF('contract-v1', templateData);
return pdf;
}
private calculateHash(contract: Contract): string {
const data = JSON.stringify({
number: contract.number,
parties: contract.parties,
terms: contract.terms,
startDate: contract.startDate,
});
return crypto.createHash('sha256').update(data).digest('hex').substring(0, 16);
}
async createDraft(contractData: Partial<Contract>): Promise<Contract> {
const contract: Contract = {
id: crypto.randomUUID(),
number: await this.generateContractNumber(),
status: 'draft',
version: 1,
...contractData as Contract,
};
await this.saveContract(contract);
return contract;
}
async sendForSignature(contractId: string): Promise<void> {
const contract = await this.getContract(contractId);
if (contract.status !== 'draft') {
throw new Error('Apenas contratos em rascunho podem ser enviados para assinatura');
}
// Gerar PDF
const pdf = await this.generateContract(contract);
// Enviar emails para assinatura
await this.sendSignatureRequest(contract.parties.contractor.email, pdf);
await this.sendSignatureRequest(contract.parties.contractee.email, pdf);
// Atualizar status
contract.status = 'pending_signature';
await this.saveContract(contract);
}
private async generateContractNumber(): Promise<string> {
const year = new Date().getFullYear();
const count = await this.getContractCount(year);
return `CTR-${year}-${String(count + 1).padStart(5, '0')}`;
}
private async saveContract(contract: Contract): Promise<void> {
// Implementar salvamento no banco
throw new Error('Not implemented');
}
private async getContract(id: string): Promise<Contract> {
// Implementar busca no banco
throw new Error('Not implemented');
}
private async sendSignatureRequest(email: string, pdf: Buffer): Promise<void> {
// Implementar envio de email
throw new Error('Not implemented');
}
private async getContractCount(year: number): Promise<number> {
// Implementar contagem
throw new Error('Not implemented');
}
}
Integração com Assinatura Eletrônica
// Integração com DocuSign, ClickSign, etc
export class SignatureService {
async sendToDocuSign(contract: Contract, pdf: Buffer): Promise<string> {
// Implementar integração com DocuSign API
const envelopeId = await docusign.createEnvelope({
document: pdf,
signers: [
{
email: contract.parties.contractor.email,
name: contract.parties.contractor.name,
},
{
email: contract.parties.contractee.email,
name: contract.parties.contractee.name,
},
],
});
return envelopeId;
}
}
Próximos Passos
- Volte para Faturas
- Veja Certificados
- Confira Relatórios