Simplificăm și accelerăm semnarea documentelor. Aplicație online completă.

Automatizează complet fluxul de semnare a documentelor: Google Gemini + eSemneaza.ro API (Ghid tehnic complet)

Semneaza electronic AI

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.

from 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 documentPOST /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 semnarePOST /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.

Articole similare