De ce contează această integrare acum
Semnarea unui contract durează, în medie, 3–5 zile când implică email-uri dus-întors, printare și curierat. Cu API-ul eSemneaza.ro poți comprima asta la câteva minute. Adaugă Google Gemini în ecuație și poți elimina și etapa de redactare manuală: documentul se generează singur, se transformă în PDF, se urcă pe platformă și se trimite la semnat — fără nicio intervenție umană.
Articolul acesta nu e o viziune de viitor. E un ghid tehnic bazat pe documentația reală a celor două API-uri, cu cod funcțional și cazuri de utilizare concrete.
Ce aduce fiecare platformă în această ecuație
Google Gemini (API)
Familia actuală de modele — Gemini 2.5 Flash (eficient și rapid), Gemini 2.5 Pro (raționament complex) și Gemini 3 Flash/Pro — are câteva capabilități direct relevante pentru automatizarea documentelor:
Structured Output (JSON Schema garantat): Modelul poate returna date structurate cu schemă predefinită, nu text liber. Asta înseamnă că poți extrage câmpuri dintr-un email de client — nume, CNP, dată_început, salariu — și obții un JSON valid, gata de inserat în template-ul contractului. Fără parsare fragilă pe text.
Trimite documente pentru semnare online
Rapid și sigur, eliminând multe dintre inconvenientele asociate cu semnarea tradițională offline sau cea cu token.
Încearcă gratuitfrom google import genai
from pydantic import BaseModel
class DateContract(BaseModel):
nume_complet: str
cnp: str
functia: str
salariu_net: float
data_angajare: str # format ISO: YYYY-MM-DD
durata_contract: str # "nedeterminata" / "determinata-12luni"
Înțelegere multimodală: Gemini poate citi un scan al unui buletin sau al unui document existent și extrage datele structurate din el — util pentru onboarding HR sau pentru actualizarea contractelor existente.
Context window larg: Modelele actuale suportă până la 1 milion de tokeni input, suficient pentru a trimite întregul manual intern de politici ca referință și a genera contracte care respectă clauzele interne.
eSemneaza.ro (API)
Platforma expune un REST API documentat la esemneaza.stoplight.io, disponibil pentru planurile Pro și Enterprise. Fluxul de bază implică două operațiuni principale:
1. Upload document — POST /upload
Acceptă fișiere în format .doc, .docx sau .pdf. Returnează un identificator unic al fișierului (filename) care se folosește la pasul următor.
2. Creare cerere de semnare — POST /request (sau echivalentul din documentație)
Trimite cererea de semnare către destinatari. Configurezi: tipul semnăturii (simplă sau avansată cu OTP SMS), ordinea semnării (secvențial sau simultan), termenul limită și mesajul personalizat.
Din punct de vedere legal, semnătura avansată cu OTP prin SMS oferă autentificarea cu două factori (email + cod SMS), conformă cu Regulamentul eIDAS (UE) nr. 910/2014 și cu legislația națională: Legea nr. 455/2001 privind semnătura electronică, actualizată prin Legea 214/2024 care a consolidat alinierea cu eIDAS la nivel de practică comercială.
Arhitectura tehnică: cum funcționează împreună
┌─────────────────────────────────────────────────────────────────┐
│ SURSĂ DATE │
│ Email client / Formular web / Scan buletin / CRM / ERP │
└──────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ GOOGLE GEMINI API │
│ • Extragere date structurate (Structured Output / Pydantic) │
│ • Validare câmpuri obligatorii │
│ • Generare text contract / act adițional │
│ • Output: JSON garantat conform schemei │
└──────────────────────────┬──────────────────────────────────────┘
│ JSON validat
▼
┌─────────────────────────────────────────────────────────────────┐
│ SERVER / FUNCȚIE BACKEND │
│ • Populare template DOCX (python-docx / jinja2) │
│ • Conversie → PDF (LibreOffice headless / WeasyPrint) │
└──────────────────────────┬──────────────────────────────────────┘
│ PDF
▼
┌─────────────────────────────────────────────────────────────────┐
│ eSemneaza.ro API │
│ Pas 1: POST /upload → returnează `filename` │
│ Pas 2: POST /request → cerere semnare + semnatari │
│ Pas 3: Webhook (opțional) → notificare după semnare │
└──────────────────────────┬──────────────────────────────────────┘
│
▼
Semnatar primește link securizat
Semnează cu OTP SMS (dacă e avansată)
Document semnat + audit trail complet
Timp total de la date brute la document trimis la semnat: sub 2 minute.
Implementare pas cu pas (Python)
Setup și dependențe
pip install google-genai python-docx pydantic requests
# Pentru conversia DOCX → PDF pe server Linux:
apt-get install libreoffice --headless
Pasul 1: Autentificare și configurare
import os
import requests
from google import genai
from pydantic import BaseModel, Field
from typing import Optional
# Gemini — noul SDK (google-genai, nu google-generativeai)
gemini_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
# eSemneaza
ESEMNEAZA_BASE = "https://app.esemneaza.ro/api"
ESEMNEAZA_HEADERS = {
"Authorization": f"Bearer {os.environ['ESEMNEAZA_TOKEN']}",
"Content-Type": "application/json",
}
Important: Token-ul API eSemneaza se generează din contul tău, secțiunea Setări → API. Nu-l include niciodată în cod sursă — folosește variabile de mediu sau un secret manager (AWS Secrets Manager, HashiCorp Vault etc.).
Pasul 2: Definire schemă și extragere date cu Gemini
class DateAngajat(BaseModel):
"""Schema pentru contractul de muncă individual."""
nume_complet: str = Field(description="Numele și prenumele angajatului")
cnp: str = Field(description="CNP-ul angajatului, 13 cifre")
adresa: str
functia: str = Field(description="Funcția conform COR")
departament: str
salariu_brut: float = Field(description="Salariul brut în RON")
data_angajare: str = Field(description="Data angajării, format YYYY-MM-DD")
durata_proba_zile: int = Field(default=90)
email_angajat: str
telefon_angajat: str = Field(description="Format internațional: +40XXXXXXXXX")
def extrage_date_din_email(email_text: str) -> DateAngajat:
"""
Extrage datele angajatului dintr-un email de confirmare angajare.
Gemini garantează un JSON conform schemei Pydantic — fără parsare fragilă.
"""
prompt = f"""
Extrage datele angajatului din următorul email și returnează-le
conform schemei solicitate. Dacă un câmp lipsește, inferă-l din context
sau lasă valoarea default. Data trebuie să fie în format ISO YYYY-MM-DD.
Email:
---
{email_text}
---
"""
response = gemini_client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config={
"response_mime_type": "application/json",
"response_json_schema": DateAngajat.model_json_schema(),
}
)
return DateAngajat.model_validate_json(response.text)
Pasul 3: Generare PDF din template
import subprocess
from docx import Document
from pathlib import Path
def genereaza_contract_pdf(date: DateAngajat, template_path: str) -> Path:
"""
Populează template-ul DOCX cu datele angajatului și îl convertește la PDF.
Template-ul folosește placeholder-e de tip {{CAMP}}.
"""
doc = Document(template_path)
substituiri = {
"{{NUME_COMPLET}}": date.nume_complet,
"{{CNP}}": date.cnp,
"{{ADRESA}}": date.adresa,
"{{FUNCTIA}}": date.functia,
"{{DEPARTAMENT}}": date.departament,
"{{SALARIU_BRUT}}": f"{date.salariu_brut:,.2f} RON",
"{{DATA_ANGAJARE}}": date.data_angajare,
"{{PERIOADA_PROBA}}": f"{date.durata_proba_zile} zile calendaristice",
}
# Înlocuiește în paragrafe și celule de tabel
for paragraph in doc.paragraphs:
for cheie, valoare in substituiri.items():
if cheie in paragraph.text:
for run in paragraph.runs:
run.text = run.text.replace(cheie, valoare)
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for cheie, valoare in substituiri.items():
if cheie in paragraph.text:
for run in paragraph.runs:
run.text = run.text.replace(cheie, valoare)
docx_path = Path(f"/tmp/contract_{date.cnp}.docx")
pdf_path = Path(f"/tmp/contract_{date.cnp}.pdf")
doc.save(docx_path)
# Conversie headless cu LibreOffice
subprocess.run([
"libreoffice", "--headless", "--convert-to", "pdf",
"--outdir", "/tmp", str(docx_path)
], check=True, capture_output=True)
return pdf_path
Pasul 4: Upload și trimitere la semnat prin eSemneaza API
def upload_si_trimite_la_semnat(
pdf_path: Path,
date: DateAngajat,
tip_semnatura: str = "advanced" # "simple" sau "advanced" (cu OTP SMS)
) -> dict:
"""
Uploadează PDF-ul la eSemneaza și creează cererea de semnare.
Returnează detaliile cererii create.
"""
# --- Pas 4a: Upload document ---
with open(pdf_path, "rb") as f:
upload_response = requests.post(
f"{ESEMNEAZA_BASE}/upload",
headers={"Authorization": ESEMNEAZA_HEADERS["Authorization"]},
files={"file": (pdf_path.name, f, "application/pdf")}
)
upload_response.raise_for_status()
filename = upload_response.json()["filename"]
# --- Pas 4b: Creare cerere de semnare ---
payload = {
"filename": filename,
"title": f"Contract de muncă — {date.nume_complet}",
"message": (
f"Bună ziua, {date.nume_complet.split()[0]}!\n\n"
"Vă transmitem contractul individual de muncă spre semnare. "
"Vă rugăm să îl parcurgeți și să îl semnați electronic. "
"Dacă aveți întrebări, nu ezitați să ne contactați."
),
"signers": [
{
"email": date.email_angajat,
"phone": date.telefon_angajat, # necesar pentru OTP SMS
"name": date.nume_complet,
"signature_type": tip_semnatura,
"order": 1 # ordinea semnării (dacă sunt mai mulți)
}
],
"expires_at": "2025-12-31T23:59:59Z", # termen limită (opțional)
"send_email": True,
}
cerere_response = requests.post(
f"{ESEMNEAZA_BASE}/request",
headers=ESEMNEAZA_HEADERS,
json=payload
)
cerere_response.raise_for_status()
return cerere_response.json()
# --- FLUX COMPLET ---
def proceseaza_angajat_nou(email_text: str, template_path: str) -> str:
"""
De la email-ul HR la contractul trimis la semnat — complet automat.
Returnează ID-ul cererii de semnare.
"""
try:
# 1. Extrage date cu Gemini (JSON garantat)
date = extrage_date_din_email(email_text)
# 2. Generează PDF din template
pdf_path = genereaza_contract_pdf(date, template_path)
# 3. Trimite la semnat
rezultat = upload_si_trimite_la_semnat(pdf_path, date)
# 4. Curăță fișierele temporare
pdf_path.unlink(missing_ok=True)
pdf_path.with_suffix(".docx").unlink(missing_ok=True)
cerere_id = rezultat.get("id", "N/A")
print(f"
Contract trimis la semnat. ID cerere: {cerere_id}")
return cerere_id
except requests.HTTPError as e:
print(f"
Eroare API eSemneaza: {e.response.status_code} — {e.response.text}")
raise
except Exception as e:
print(f"
Eroare generală: {e}")
raise
Capcanele tehnice pe care trebuie să le eviți
Înainte de a ajunge la use cases, câteva erori frecvente care strică integrările altfel bine gândite:
1. SDK-ul Gemini vechi vs. nou
Codul din multe tutoriale folosește google-generativeai (vechiul SDK) cu genai.GenerativeModel(). SDK-ul actual este google-genai cu genai.Client(). Diferența nu e doar sintactică — noul SDK suportă nativ Pydantic și response_json_schema, eliminând nevoia de parsare manuală.
#
Vechi (google-generativeai)
import google.generativeai as genai
model = genai.GenerativeModel('gemini-1.5-flash')
response = model.generate_content(prompt)
#
Nou (google-genai)
from google import genai
client = genai.Client(api_key="...")
response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
2. Generare text fără schemă = parsare fragilă
Dacă ceri Gemini să genereze “un JSON cu datele contractului” în prompt (fără response_json_schema), modelul poate adăuga text introductiv, poate folosi formate inconsistente sau poate omite câmpuri. Cu schema Pydantic, output-ul e garantat sau primești eroare — nu date parțiale silențioase.
3. Conversia DOCX → PDF pe server
LibreOffice în modul headless e soluția cea mai robustă pentru server Linux. Alternativele cloud (Google Docs API, Adobe PDF Services) adaugă latență și costuri. Asigură-te că LibreOffice poate accesa fonturile folosite în template — fonturile lipsă produc documente cu aspect diferit față de așteptări.
4. Gestionarea timeout-urilor
Dacă volumul e mare, nu face upload-urile secvențial. Folosește concurrent.futures.ThreadPoolExecutor cu un număr rezonabil de workeri (4–8) și implementează retry cu backoff exponențial pentru erorile tranzitorii de rețea.
5. Date personale și GDPR
Datele angajaților (CNP, adresă, salariu) care trec prin Gemini API sunt procesate de Google. Verifică termenii de utilizare a datelor pentru API-ul Gemini Enterprise și asigură-te că ai baza legală pentru procesare (art. 6 GDPR). Consideră anonimizarea datelor de test în mediile de dezvoltare.
Top 3 use cases cu impact măsurabil
1. Onboarding HR automatizat
Problema: Departamentul HR pierde în medie 45 de minute per angajat nou cu redactarea contractului, completarea manuală a câmpurilor și trimiterea spre semnare. La 50 de angajări pe lună, asta înseamnă ~37 de ore de muncă repetitivă.
Soluția cu Gemini + eSemneaza:
– Recruiterul completează un formular web simplu (sau trimite un email cu datele candidatului)
– Gemini extrage și validează câmpurile cu schema Pydantic
– Template-ul DOCX e populat automat (contract individual de muncă, fișă post, acord GDPR)
– Pachetul de documente e trimis candidatului pentru semnare cu OTP SMS
– Managerul și reprezentantul legal primesc documentele spre co-semnare secvențial
Timp redus: de la 45 de minute la sub 3 minute per angajat. Erori de tastare: eliminate.
Ce mai poți face: Gemini poate extrage date direct dintr-o poză a buletinului (OCR multimodal), eliminând și completarea formularului.
2. Procurement și aprobarea comenzilor de achiziție
Problema: Furnizorii trimit oferte în formate diferite (PDF, email, Word). Compararea manuală, aprobarea și emiterea comenzii formale durează 2–3 zile și implică mai mulți oameni.
Soluția:
class Oferta(BaseModel):
furnizor: str
pret_unitar: float
cantitate: int
pret_total: float
termen_livrare_zile: int
valabilitate_oferta_zile: int
conditii_plata: str # ex: "30 zile de la facturare"
def analizeaza_si_aproba_oferta(pdf_oferta: bytes, buget_maxim: float) -> str:
"""
Gemini citește PDF-ul ofertei și returnează datele structurate.
Dacă prețul se încadrează în buget, emite comanda automat.
"""
response = gemini_client.models.generate_content(
model="gemini-2.5-flash",
contents=[
{"inline_data": {"mime_type": "application/pdf", "data": pdf_oferta}},
{"text": "Extrage datele ofertei conform schemei solicitate."}
],
config={
"response_mime_type": "application/json",
"response_json_schema": Oferta.model_json_schema(),
}
)
oferta = Oferta.model_validate_json(response.text)
if oferta.pret_total <= buget_maxim:
# Generează comanda de achiziție și trimite la eSemneaza
return genereaza_si_trimite_comanda(oferta)
else:
return f"Oferta depășește bugetul ({oferta.pret_total} > {buget_maxim} RON). Necesită aprobare manuală."
Impact: Comenzile sub pragul de buget aprobat se procesează complet automat. Doar excepțiile ajung la manager — care semnează deja pe eSemneaza, din telefon.
3. Actualizare în masă a contractelor (acte adiționale)
Problema: O modificare legislativă (sau o actualizare internă a politicilor) necesită trimiterea de acte adiționale la toți angajații sau partenerii. La 500 de contracte, procesul manual poate dura săptămâni.
Soluția:
def genereaza_acte_aditionale_bulk(
lista_angajati: list[dict],
modificarea: str,
data_intrare_vigoare: str
) -> list[str]:
"""
Generează și trimite acte adiționale personalizate pentru toți angajații.
Returnează lista ID-urilor cererilor de semnare.
"""
ids_cereri = []
for angajat in lista_angajati:
# Gemini personalizează textul actului adițional per angajat
prompt = f"""
Generează textul unui act adițional la contractul individual de muncă pentru:
Angajat: {angajat['nume']}
Funcție: {angajat['functia']}
Modificare: {modificarea}
Data intrării în vigoare: {data_intrare_vigoare}
Folosește limbaj juridic formal, conform legislației române a muncii.
Actul se va integra în contractul existent, nu îl înlocuiește.
"""
text_act = gemini_client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt
).text
# Generează PDF și trimite
pdf = genereaza_pdf_din_text(text_act, angajat)
rezultat = upload_si_trimite_la_semnat(pdf, angajat)
ids_cereri.append(rezultat["id"])
return ids_cereri
Impact: 500 de acte adiționale generate și trimise la semnat în câteva ore, nu săptămâni. eSemneaza oferă trasabilitate completă — știi exact cine a semnat, când și de pe ce dispozitiv.
Monitorizare: cum știi că totul merge bine
Webhook-uri eSemneaza
eSemneaza poate trimite notificări HTTP (webhook) la URL-ul tău atunci când:
– Un semnatar accesează documentul
– Semnătura e aplicată
– Toți semnatarii au semnat (documentul e finalizat)
– Termenul limită a expirat fără semnare
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/webhook/esemneaza", methods=["POST"])
def webhook_esemneaza():
event = request.json
if event["event"] == "document.completed":
cerere_id = event["request_id"]
# Arhivează documentul semnat, actualizează CRM/ERP, notifică HR
proceseaza_document_finalizat(cerere_id)
elif event["event"] == "signer.expired":
# Retrimite cererea sau escaladează
gestioneaza_expirare(event)
return jsonify({"status": "ok"}), 200
Metrici de monitorizat
- Rata de succes a extragerii Gemini: dacă schema Pydantic validează fără erori — vizează >98%
- Timp de procesare end-to-end: upload + creare cerere ar trebui să fie sub 10 secunde
- Rata de semnare: câte cereri sunt semnate în primele 24h — indicator de calitate UX
- Erori API eSemneaza: logează toate răspunsurile non-2xx cu payload-ul trimis
Ce nu se automatizează (și de ce e bine)
Automatizarea completă e eficientă, dar există contexte unde intervenția umană rămâne necesară:
- Contracte cu negociere: dacă termenii nu sunt standard, un om trebuie să revizuiască înainte de trimitere
- Documente cu valoare juridică ridicată: fuziuni, cesiuni de drepturi, contracte cu statul — merită o revizuire juridică chiar dacă redactarea e automată
- Situații ambigue: dacă Gemini nu poate extrage un câmp obligatoriu cu încredere suficientă, e mai bine să escaladezi decât să trimiți date incorecte
O regulă bună: automatizează fluxul standard, menține un canal de excepții cu alertă imediată și posibilitate de intervenție manuală rapidă.
Concluzie
Combinația Gemini + eSemneaza nu e o optimizare marginală — e o restructurare a modului în care documentele juridice și administrative circulă în organizație. Redactarea, popularea, trimiterea și urmărirea semnărilor devin un proces unic, tracabil și reproductibil.
Costul implementării unui astfel de flux e de ordinul a câteva zile de dezvoltare. Câștigul se măsoară în ore salvate zilnic și erori umane eliminate sistemic.
Pasul următor: consultă documentația completă a API-ului eSemneaza la esemneaza.stoplight.io și obține token-ul API din contul tău Pro sau Enterprise. Pentru Gemini, cheia API e disponibilă gratuit din Google AI Studio.
Articol bazat pe documentația oficială eSemneaza REST API (Stoplight), Google Gemini API (google-genai SDK), Regulamentul eIDAS (UE) 910/2014 și Legea 214/2024.