signit

API pentru rezultatele câmpurilor

Citește din propriile sisteme câmpurile și răspunsurile trimise pentru documentele tale.

Signit are trei endpointuri HTTP doar pentru citire, pentru câmpurile Text, Casetă de bifare și Semnătură ale documentelor pe care le trimiți. Folosește-le ca să copiezi răspunsurile într-un CRM, ERP sau tabel. Ele returnează doar răspunsurile pe care un semnatar le-a trimis deja; răspunsurile netrimise rămân private pentru semnatar. Ciornele nu sunt vizibile.

Un token de citire poate doar să citească. Nu poate crea, trimite, semna sau modifica nimic.

Prima cerere în cinci minute

Pornește autentificat în Signit, cu cel puțin un document pe care un semnatar l-a terminat.

  1. Deschide Conexiune agent → API pentru rezultatele câmpurilor (sau mergi la https://app.sign-it.ro/settings/api). Dă un nume tokenului, alege cât durează și apasă Creează tokenul.
  2. Copiază tokenul. Signit îl afișează o singură dată și păstrează doar o amprentă.
  3. Salvează-l în variabila de mediu SIGNIT_READ_TOKEN, apoi listează documentele modificate recent:
curl -sS -H "Authorization: Bearer $SIGNIT_READ_TOKEN" \
  "https://app.sign-it.ro/api/v1/envelopes?limit=10"
const response = await fetch("https://app.sign-it.ro/api/v1/envelopes?limit=10", {
  headers: { Authorization: `Bearer ${process.env.SIGNIT_READ_TOKEN}` },
});
if (!response.ok) throw new Error(`Signit returned ${response.status}`);
const page = await response.json();
console.log(page.data);
$headers = @{ Authorization = "Bearer $env:SIGNIT_READ_TOKEN" }
Invoke-RestMethod -Uri "https://app.sign-it.ro/api/v1/envelopes?limit=10" -Headers $headers
  1. Ia un id din data și citește valorile trimise. Fiecare pagină de document din Signit afișează aceste cereri, gata de copiat, la Rezultatele câmpurilor.
curl -sS -H "Authorization: Bearer $SIGNIT_READ_TOKEN" \
  "https://app.sign-it.ro/api/v1/envelopes/ENVELOPE_ID/field-results"
const response = await fetch("https://app.sign-it.ro/api/v1/envelopes/ENVELOPE_ID/field-results", {
  headers: { Authorization: `Bearer ${process.env.SIGNIT_READ_TOKEN}` },
});
if (!response.ok) throw new Error(`Signit returned ${response.status}`);
const results = await response.json();
console.log(results.values);
$headers = @{ Authorization = "Bearer $env:SIGNIT_READ_TOKEN" }
Invoke-RestMethod -Uri "https://app.sign-it.ro/api/v1/envelopes/ENVELOPE_ID/field-results" -Headers $headers

Ținta de cinci minute include crearea tokenului și aceste cereri. Crearea contului și semnarea unui exemplu nu fac parte din ea.

Tokenuri de citire

  • Doar pentru citire, pentru documentele organizației selectate în momentul cererii. Dacă schimbi organizația, același token citește din cealaltă.
  • Valabile implicit 30 de zile, cel mult 90. Poți avea 10 tokenuri active.
  • Revocă un token oricând din API pentru rezultatele câmpurilor; următoarea cerere eșuează.
  • Fiecare cerere este verificată cu contul tău așa cum este acum. Dacă contul este șters sau nu mai poate trimite, tokenurile lui nu mai funcționează.
  • Tokenurile de citire și conexiunile de agent sunt separate: conectorul de agent nu acceptă un token de citire, iar aceste endpointuri nu acceptă un secret de agent.
  • Păstrează tokenul într-un depozit de secrete. Răspunsurile de citire, URL-urile și jurnalele nu îl conțin.

Trimite-l ca Authorization: Bearer <token>. Fiecare răspuns are Cache-Control: private, no-store.

Endpointuri

GET /api/v1/envelopes

Descoperire: documentele tale trimise, în ordinea (updatedAt, id).

ParametruSemnificație
updatedAfterOpțional, moment RFC 3339, inclusiv, de exemplu 2026-10-16T09:30:00.000000Z.
limit1–100, implicit 50.
cursornextCursor din pagina anterioară. Repetă aceiași alți parametri sau niciunul.

Răspunsul conține data (fiecare element: id, status, subject, externalReference, signingFormatVersion, definitionRevision, updateRevision, updatedAt, sentAt, completedAt), hasMore, nextCursor și scanThrough. Momentele sunt milisecunde întregi de la ceasul aplicației Signit, scrise cu șase zecimale; ultimele trei sunt mereu 000. updatedAfter păstrează doar milisecunde întregi: partea sub-milisecundă se elimină, deci filtrul poate întoarce puțin mai mult decât ai cerut. Deduplică după updateRevision, cum arată pașii de mai jos.

GET /api/v1/envelopes/{id}/fields

Definiția câmpurilor înghețată la trimitere: pentru fiecare câmp id, key, type, label, required, signerId-ul semnatarului, poziția, iar pentru câmpurile Text multiline și prefill. Un prefill cu locked: true este textul expeditorului: face parte din definiție și nu este niciodată un răspuns al semnatarului. Perechile tale metadata apar aici și nu ajung niciodată la semnatari. signers listează semnatarii, iar envelope.definitionRevision identifică această definiție.

GET /api/v1/envelopes/{id}/field-results

Doar valorile trimise:

  • values: câte o intrare pentru fiecare câmp al fiecărui semnatar care a terminat, cu fieldId, key, type, value, provenance, actorKind, signerId și submittedAt. Câmpurile Semnătură conțin numele adoptat.
  • signers: fiecare semnatar cu status, completedAt, actorKind și auditEventId-ul finalizării sale.
  • state.signing: in_progress, completed (toți semnatarii au terminat), declined, expired, voided sau failed. state.terminal este true când nu mai pot sosi răspunsuri.
  • state.artifacts: not_applicable până se termină semnarea, apoi pending, failed dacă generarea necesită o reîncercare, apoi ready cu amprentele PDF-ului final și ale certificatului. Semnare terminată și documente gata sunt momente diferite.

Răspunsurile unui semnatar care nu a terminat nu apar niciodată, deci rezultatele pot fi parțiale cât timp semnarea este în curs.

Pentru documentele care au numai semnături, identitățile câmpurilor provin din dovezile finalizării și folosesc cheile înghețate. Valorile includ source: "legacy_signature_evidence" și evidence: { signerId, auditEventId }. Dacă identitățile unei finalizări mai vechi nu pot fi recuperate, răspunsul folosește projectionStatus: "legacy_evidence_only" cu signerSignatures, fără să inventeze ID-uri. Dovezile trimise rămân disponibile.

provenanceSemnificație
signer_enteredSemnatarul l-a scris sau ales. false aici este un „nu” explicit.
sender_prefill_confirmedTextul sugerat de expeditor, lăsat neschimbat și confirmat.
not_providedText opțional omis: value este null. "" înseamnă lăsat gol explicit.
unchecked_defaultCasetă opțională neatinsă: value este false.
adopted_signatureNumele adoptat de semnatar pentru un câmp Semnătură.

actorKind este human_ui când semnatarul a folosit Signit sau agent_credential când agentul său conectat a semnat pentru el.

Fiecare răspuns este citit dintr-un singur instantaneu consecvent al bazei de date. Două cereri pot diferi dacă ceva s-a schimbat între ele: compară definitionRevision când combini fields cu field-results.

Referință externă

Pe pagina unui document, în cardul Referință externă, poți salva propriul ID pentru el (cel mult 200 de caractere). Revine ca externalReference, iar schimbarea lui contează ca actualizare.

Interogare periodică pentru modificări

Descoperirea este interogare periodică limitată, nu un flux garantat de evenimente: o modificare poate apărea cu întârziere, iar un document care lipsește dintr-o pagină nu este șters.

  1. Prima rulare scanează fără updatedAfter.
  2. Urmează nextCursor până când hasMore este false. Salvează scanThrough-ul scanării ca punct de control doar după ce ai procesat fiecare pagină.
  3. Următoarea rulare cere updatedAfter = punctul de control minus cinci minute.
  4. Sari peste un document al cărui updateRevision l-ai procesat deja. Crește la fiecare modificare vizibilă: un răspuns trimis, un status final, documente gata, o referință externă nouă.
  5. O dată pe zi, scanează din nou fără updatedAfter pentru reconciliere.

Un cursor este valabil 15 minute, pentru același titular de token și aceiași parametri. După invalid_cursor sau cursor_expired, pornește din nou rularea de la punctul de control.

Rulează jobul următor o dată pe minut, fără execuții simultane. Furnizează funcțiile persistente handle și saveState descrise în exemplu. Repornește cursoarele expirate de cel mult trei ori și face o scanare completă cel puțin zilnic. O pagină ulterioară eșuată păstrează ultimul checkpoint complet. Oprește la 401, corectează parametrii 400 și reîncearcă la următoarea execuție după o întrerupere.

Un exemplu complet:

// signit-poll-example
const OVERLAP_MS = 5 * 60 * 1000;
const RECONCILE_MS = 24 * 60 * 60 * 1000;

async function getJson(url, token, attempt = 0) {
  const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
  if ((response.status === 429 || response.status === 503) && attempt < 5) {
    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs =
      Number.isFinite(retryAfter) && retryAfter > 0
        ? Math.min(60_000, Math.max(1_000, retryAfter * 1000))
        : Math.min(60_000, 1000 * 2 ** attempt);
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    return getJson(url, token, attempt + 1);
  }
  const body = await response.json();
  if (!response.ok) {
    throw Object.assign(new Error(body.error?.message ?? `Signit returned ${response.status}`), {
      code: body.error?.code,
      status: response.status,
    });
  }
  return body;
}

// Run once per minute with the latest durably loaded state, without concurrent runs.
// state = { checkpoint: null, lastReconciledAt: null, seen: {} }.
// handle must durably apply each idempotencyKey at most once. A crash between
// handle and saveState can repeat it. saveState must atomically persist its argument.
async function pollSignit({
  baseUrl,
  token,
  state,
  handle,
  saveState,
  now = Date.now,
  limit = 100,
}) {
  const fullScan =
    !state.checkpoint ||
    !state.lastReconciledAt ||
    now() - Date.parse(state.lastReconciledAt) >= RECONCILE_MS;
  const filters = new URLSearchParams({ limit: String(limit) });
  if (!fullScan) {
    filters.set("updatedAfter", new Date(Date.parse(state.checkpoint) - OVERLAP_MS).toISOString());
  }
  const persist = async (next) => {
    await saveState(structuredClone(next));
    Object.assign(state, next);
  };
  for (let restart = 0; ; restart++) {
    let cursor = null;
    let scanThrough = null;
    try {
      do {
        const query = new URLSearchParams(filters);
        if (cursor) query.set("cursor", cursor);
        const page = await getJson(`${baseUrl}/api/v1/envelopes?${query}`, token);
        scanThrough = page.scanThrough;
        for (const envelope of page.data) {
          if ((state.seen[envelope.id] ?? -1) >= envelope.updateRevision) continue;
          let results;
          try {
            results = await getJson(
              `${baseUrl}/api/v1/envelopes/${envelope.id}/field-results`,
              token,
            );
          } catch (error) {
            // Unavailable to this credential is not proof of deletion.
            if (error.status === 404) continue;
            throw error;
          }
          const revision = results.envelope.updateRevision;
          await handle(results, { idempotencyKey: `${envelope.id}@${revision}` });
          // Business data first, then durable dedupe, with the old checkpoint.
          await persist({ ...state, seen: { ...state.seen, [envelope.id]: revision } });
        }
        cursor = page.nextCursor;
      } while (cursor);
      // Only a fully processed scan advances either completed checkpoint.
      await persist({
        ...state,
        checkpoint: scanThrough,
        lastReconciledAt: fullScan ? new Date(now()).toISOString() : state.lastReconciledAt,
      });
      return state;
    } catch (error) {
      if (["invalid_cursor", "cursor_expired"].includes(error.code) && restart < 3) continue;
      // Keep the last completed checkpoint on outage, bad parameters or lost authority.
      // Correct invalid_parameter input or renew credentials before the next scheduled run.
      throw error;
    }
  }
}

Erori

Erorile arată astfel: { "error": { "code": "...", "message": "...", "requestId": "..." } }.

StatuscodeCe faci
400invalid_parameterCorectează updatedAfter sau limit ori elimină parametrii necunoscuți.
400invalid_cursor, cursor_expiredPornește rularea din nou de la punctul de control.
401unauthenticated, invalid_tokenTrimite un token de citire valid, neexpirat și nerevocat.
404not_foundDocumentul nu există, nu este al tău sau este încă ciornă.
429—Așteaptă Retry-After sau reîncearcă de la 1 s, dublând până la 60 s.
503unavailableAceeași reîncercare limitată; oprește-te după câteva încercări.
500internal_errorReîncearcă mai târziu; menționează requestId dacă persistă.

On this page