FR EN

Record an offline consent

Not every person has a passkey. An elderly person who signs a paper form will never go through the consent request approval flow. For that case there is a separate path: the company records a consent from a hand-signed PDF, under the attest scope. The result is a company-attested consent that enforces exactly like a citizen-signed one.

You need an attest-scoped key.

You start from a hand-signed PDF and the published version of the disclosure the person consented under. The SDK hashes the PDF locally (SHA-256); by default, only the pdfSha256 leaves your machine. The raw bytes leave only with an explicit upload.

const m = await agreely.manualConsents.record({
  customerId: "cust_8812",
  documentVersionId: "doc_v3",     // the PUBLISHED disclosure version signed under
  effectiveDate: "2026-01-01",
  validUntil: "2031-01-01",
  items: [{ category: "Phone number", purpose: "Billing" }],
  evidence: {
    pdfSha256: Agreely.hashPdf(bytes), // "0x"+sha256, hashed LOCALLY, required
    // pdf: base64Pdf,                 // optional: only with an explicit upload
  },
});

m.assurance; // "company_attested" m.consentRefs; // one 0x ref per cell; m.anchored is false at record time

$m = $agreely->manualConsents()->record([
    'customerId'        => 'cust_8812',
    'documentVersionId' => 'doc_v3',
    'effectiveDate'     => '2026-01-01',
    'validUntil'        => '2031-01-01',
    'items'             => [['category' => 'Phone number', 'purpose' => 'Billing']],
    'evidence'          => [
        'pdfSha256' => Agreely::hashPdf($bytes), // "0x"+sha256, LOCAL, required
        // 'pdf'    => $base64Pdf,               // optional opt-in upload
    ],
]);

$m->assurance; // "company_attested" $m->consentRefs; // one 0x ref per cell

agreely manual-consent create \
  --customer cust_8812 --document-version doc_v3 \
  --effective-date 2026-01-01 --valid-until 2031-01-01 \
  --item "Phone number:Billing" \
  --pdf ./signed-consent.pdf --json
# the hash alone is sent; add --upload to also escrow the raw bytes
curl -sS https://api.agreely.ca/v1/manual-consents \
  -H "Authorization: Bearer $AGREELY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "cust_8812",
    "documentVersionId": "doc_v3",
    "effectiveDate": "2026-01-01",
    "validUntil": "2031-01-01",
    "items": [{"category":"Phone number","purpose":"Billing"}],
    "evidence": {"pdfSha256":"0x…"}
  }'

The response carries consentId, merkleRoot, a consentRefs (one 0x-hex per cell), assurance: "company_attested", and anchored: false (anchoring happens afterward, asynchronously). Agreely.hashPdf is static and calls no network; it produces exactly the shape evidence.pdfSha256 expects.

It enforces identically

A company-attested consent and a citizen-signed consent authorize identically at /v1/check. Only the assurance tier differs: a check returns assurance: "company_attested" instead of "citizen_signed". The code that gates a feature changes nothing.

The company can issue a single-use claim link so the person can later attach this offline consent to their own decentralized identifier.

const link = await agreely.manualConsents.createClaimLink({
  customerId: "cust_8812",
  reference: "order-4471", // optional
});
// { claimUrl, token, expiresAt } - single-use

On the CLI: agreely manual-consent claim-link --customer cust_8812 --reference order-4471.

The honest boundary

An attested consent does not prove a human signed

A company_attested consent proves that the company attests it holds a signed PDF whose hash matches what was recorded. It does not prove that a human actually signed the document, nor who signed it: there is no passkey assertion from the person. It is a legitimate path for the no-passkey case, but its assurance tier is weaker than a citizen-signed consent, which is why the assurance field names it explicitly everywhere.

Next

  • Consent requests: the citizen-signed path, and the company-side context for offline consent.
  • Citizen portal: what the person sees when they claim and confirm their consent.
  • Verify a receipt: a company_attested receipt verifies fully offline.