Skip to content

PDF Seals ​

Integrate with an AI assistant
Download or copy the integration prompt for PDF Seals and paste it into Claude Code, Cursor or Copilot: it contains auth, end-to-end examples and a checklist.
Open

Applies one or more seals to a PDF and records its certification (number, date and sealed document). It is the same function used by the rent-agreement document certification: same engine, same result, exposed as an API call.

There are two ways to call it:

  • POST /api/partner/certifications — the call that covers both cases. The bundle is an optional field: with user_bundle_id the certification stays recorded on that practice (and if the certification step exists the document is filed among its attachments too); without it, you just send us the document, which is what you need when whatever you are certifying has no DokiCasa practice behind it.
  • POST /api/partner/user-bundle/{id}/seal — the original route, bundle only. It stays where it is: if you have already integrated it, nothing changes for you.
Base URLhttps://api.dokicasa.it
AuthAuthorization: Bearer <api_token>
Content-Typemultipart/form-data (with upload) or application/json (with attachments)

The call is synchronous: by the time it responds, the sealed PDF is ready and recorded.

WARNING

Not available on the sandbox: sealing goes through the qualified signature provider, so every call produces a real document and is billed.

Prerequisites ​

  1. Active pricing for the SEAL_PDF type on your account. Without it the call returns 403 PARTNER_PRICING_REQUIRED: contact us to enable it.
  2. A bundle (user_bundle) owned by your user, only for the call on a bundle. The certification step does not need to exist. The bundle id is returned by practice creation — POST /api/v3/form/{slug} — in the user_bundle_id field. POST /api/partner/certifications needs no bundle at all.
  3. At least one seal registered on your account, on your parent account, or belonging to the certification body (the association) you are entitled to certify for — in that case the seal may live on a different account.

Your seals ​

GET /api/users/me/seals

Lists the available seals: yours, your parent account's if you have one, and those of the certification body you are entitled to certify for.

json
[
  {
    "id": 42,
    "first_name": "Mario",
    "last_name": "Rossi",
    "email": "mario.rossi@example.it",
    "image": "sigilli/mario-rossi.png",
    "is_verified": true,
    "certification_body_id": null,
    "certification_body_name": null
  },
  {
    "id": 57,
    "first_name": "Associazione",
    "last_name": "Proprietari",
    "email": "info@associazione.it",
    "image": "sigilli/associazione.png",
    "is_verified": true,
    "certification_body_id": 3,
    "certification_body_name": "Associazione Proprietari"
  }
]

The id from this list is the one to pass when applying seals.

certification_body_id and certification_body_name tell you which body you are certifying on behalf of with that seal: the number, the footer text and the seal position on the page are dictated by the body. They are null for seals that belong to no body, which behave exactly as they always have.

If your account is enabled for more than one body, the list contains the seals of all of them, each with its own body.

Applying the seals ​

POST /api/partner/user-bundle/{user_bundle_id}/seal

FieldTypeNotes
seals[].idintegerOne or more seals, applied in the given order. Required unless you pass seal_id
seal_idintegerShorthand for the single-seal case, alternative to seals[]
filefilePDF to seal, max 20 MB. When present, it takes precedence over attachments
attachments[].urlstringAlternative to the file: public URLs of the PDFs to seal, merged into a single document
attachments[].namestringAttachment name, optional (shown in error messages)

The seal must belong to you, to your parent account or to the certification body you are entitled to certify for, and the bundle must be yours — otherwise 403.

With an uploaded PDF (two seals) ​

bash
curl -X POST https://api.dokicasa.it/api/partner/user-bundle/12345/seal \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "file=@contract.pdf" \
  -F "seals[0][id]=42" \
  -F "seals[1][id]=57"

With attachments already online (one seal) ​

bash
curl -X POST https://api.dokicasa.it/api/partner/user-bundle/12345/seal \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "seals": [{ "id": 42 }],
        "attachments": [
          { "name": "Contract", "url": "https://your-storage/contract.pdf" },
          { "name": "Annex A", "url": "https://your-storage/annex-a.pdf" }
        ]
      }'

Response — 201 Created ​

json
{
  "status": "READY",
  "numero_certificazione": "12/2026",
  "document": {
    "name": "certificazione_12345_12.pdf",
    "path": "https://r3-it.storage.cloud.it/storage/bundles-certifications/12345/certificazione_12345_12.pdf"
  },
  "seals": [
    {
      "id": 7,
      "certification_body_id": 3,
      "certification_body_name": "Associazione Proprietari"
    }
  ]
}

seals lists the applied seals, each with its body (null if the seal belongs to no body).

The certification is recorded on the bundle. If the bundle already has the certification step, the same document is also added to that step's practice attachments, named Documento Certificato: you can read it back from GET /api/user-service/{id}/attachments.

Certifying with or without a bundle ​

POST /api/partner/certifications

Same sealing, same numbering, same document out. The bundle is your choice, field by field: pass user_bundle_id and the certification is recorded on that practice exactly as on the seals route; leave it out and there is no DokiCasa practice behind it — no step to prepare, no bundle to create first.

FieldTypeNotes
external_idstringRequired. The certification id on your own system, max 100 characters
user_bundle_idintegerOptional. The bundle to record the certification on. Leave it out to certify a document that has no practice on our side
seals[].idintegerOne or more seals, applied in the order received. Required unless you pass seal_id
seal_idintegerShortcut for the single-seal case
filefilePDF to certify, max 20 MB. When uploaded, it takes precedence over attachments
attachments[].urlstringInstead of the file: public URLs of the PDFs, merged into a single document
attachments[].namestringAttachment name, optional
bash
# Your document, no practice on our side
curl -X POST https://api.dokicasa.it/api/partner/certifications \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "external_id=CONTRACT-2026-0031" \
  -F "file=@contract.pdf" \
  -F "seals[0][id]=42"

# Same call, recorded on one of our bundles
curl -X POST https://api.dokicasa.it/api/partner/certifications \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "external_id=CONTRACT-2026-0031" \
  -F "user_bundle_id=12345" \
  -F "file=@contract.pdf" \
  -F "seals[0][id]=42"

With user_bundle_id the response carries that bundle, the document is recorded on the practice and — if the certification step exists — filed among its attachments as Documento Certificato. The bundle must be yours, otherwise 403 BUNDLE_NOT_OWNED.

Response — 201 Created ​

json
{
  "id": 918,
  "status": "READY",
  "external_id": "CONTRACT-2026-0031",
  "numero_certificazione": "13/2026",
  "number": 13,
  "year": 2026,
  "certified_at": "2026-09-18 11:20:44",
  "user_bundle_id": null,
  "document": {
    "name": "certificazione_doc-918_13.pdf",
    "path": "https://r3-it.storage.cloud.it/storage/certifications/918/certificazione_doc-918_13.pdf"
  },
  "documents": [{ "name": "certificazione_doc-918_13.pdf", "path": "..." }],
  "seals": [{ "id": 42, "certification_body_id": 3, "certification_body_name": "Owners Association" }]
}

external_id, or: never certify twice ​

Every call produces a real qualified signature and consumes a serial number. external_id is your protection against duplicates: call again with the same key and you get back the certification already issued — 200 OK instead of 201 — without signing again and without being charged twice.

So a network timeout can be retried without a second thought: what matters is sending the same key. If a certification with that key is still being processed the answer is 409 CERTIFICATION_IN_PROGRESS; if an earlier attempt failed, the new one keeps the number already assigned instead of burning another.

Reading certifications back ​

GET /api/partner/certifications/{id}
GET /api/partner/certifications?external_id=...&status=READY&per_page=25

The list is your own register, most recent first, with data and meta (current_page, last_page, per_page, total). It covers bundle certifications too: the .../seal response carries certification_id, which is the id to use here.

What the document looks like ​

  • Multiple attachments are merged into a single PDF, in the order you pass them.
  • Every page gets the Certificazione N/YYYY label in the top-left corner, with the text of the certifying body underneath (for associations that signed the territorial agreement it is the usual wording).
  • Seals are applied on every page, at the bottom, left to right: two seals sit side by side, not one on top of the other.
  • A single seal may sit on the right instead, when it is a tenants' association seal: that is how two associations certify the same document, one at a time, each from its own side.
  • The same seal passed twice is applied only once.

Numbering ​

The certification number is progressive per year. The series belongs to the certification body, when the seals used belong to one; otherwise it belongs to the parent account, or to you if you have no parent. Every series starts from 1.

A bundle that already has a number keeps it: sealing the same bundle again (for example after fixing an attachment) updates the document without burning a new number and without leaving gaps in the series. Without a bundle the same holds for external_id: that is the key that ties the attempts together.

PDF requirements ​

Every page must be A4 portrait: seals are placed at fixed coordinates, so a different format would put them out of place. Landscape or A3 documents are rejected with 422 NOT_A4, telling you which attachment is out of format and the detected size.

Errors ​

HTTPerrorWhen
403PARTNER_PRICING_REQUIREDNo SEAL_PDF pricing configured for your account
403BUNDLE_NOT_OWNEDThe bundle does not belong to your user
403CERTIFICATION_NOT_OWNEDThe certification you are reading back is not yours
409CERTIFICATION_IN_PROGRESSA certification with the same external_id is being processed
403SEAL_NOT_OWNEDOne of the seals is neither yours, nor your parent's, nor your body's
400AMOUNT_NOT_ENOUGHNot enough wallet balance. The response includes missing_amount and checkout_link
422NO_DOCUMENTNeither file nor attachments: nothing to seal
422NOT_A4An attachment is not A4 portrait

On top of these, the call returns 422 with message and errors (standard validation format) when the input is not acceptable in the first place:

  • attachments[].url must be http or https pointing at a public address: internal, private or other-scheme URLs are rejected;
  • at most 10 seals and 20 attachments per call;
  • external_id is required and no longer than 100 characters;
  • user_bundle_id, when passed, must be an existing, non-deleted bundle.

NOTE

You are charged only once the document is produced: if sealing fails, the call returns an error and nothing is deducted.