Build Portable Document templates

Turn an uploaded PDF into a Portable Document template, then rename, delete, fetch, or list a project's templates.


A Portable Document is a PDF template scoped to a workspace. You upload a PDF, Blue rasterizes each page into a PNG, and the resulting template becomes a canvas you overlay record fields onto so you can later print a filled PDF for any specific record. Workspaces are Project objects in the API, and a template is a PortableDocument.

This is a separate subsystem from the collaboratively-edited rich-text Documents — they share the “Documents” section but no model, no fields, and no operations. Portable Documents are about laying record data onto a fixed PDF layout; rich-text Documents are about editing prose.

This page covers the template lifecycle: create one from an uploaded file, rename it, delete it, fetch one, list a workspace’s templates, and link a template to record tables. Placing the overlay fields and printing a filled PDF are covered in Map fields and print Portable Documents.

Create a template

Use the createPortableDocument mutation to build a template from a PDF that has already been uploaded. You pass the workspace and the uploaded file; Blue fetches the file from storage, renders each page to a PNG, and stores the pages on the new template.

mutation CreatePortableDocument {
  createPortableDocument(input: { projectId: "project_123", fileId: "file_123" }) {
    id
    uid
    name
    pages {
      id
      url
    }
    createdBy {
      id
      fullName
    }
  }
}

The name is taken from the source file’s name — there is no name argument on create. Use updatePortableDocument afterwards to rename it.

CreatePortableDocumentInput

ParameterTypeRequiredDescription
projectIdString!YesWorkspace the template belongs to. Accepts the project ID or slug.
fileIdString!YesThe uploaded PDF to convert. Resolved from the upload, so a file ID, storage key, or URL is accepted.

See Upload files for how to obtain a fileId.

Response

{
  "data": {
    "createPortableDocument": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "uid": "pdf_clm4n8qwx000108l0h2abc456",
      "name": "Invoice template.pdf",
      "pages": [
        { "id": "clm4n8qwx000208l0p1ghi789", "url": "pdf_clm4n8qwx000108l0h2abc456/page_1" },
        { "id": "clm4n8qwx000308l0p2jkl012", "url": "pdf_clm4n8qwx000108l0h2abc456/page_2" }
      ],
      "createdBy": { "id": "clm4n8qwx000408l0u1mno345", "fullName": "Ada Lovelace" }
    }
  }
}

The PortableDocument type

FieldTypeDescription
idID!Unique template identifier.
uidString!Prefixed unique key (pdf_…); also the storage prefix for the page images.
nameString!Display name, derived from the source file.
isActiveBoolean!Whether the template is active. Inactive templates cannot be printed or exported; they stay editable in settings. New templates are active.
createdAtDateTime!When the template was created.
updatedAtDateTime!When the template was last modified.
createdByUser!The user who created the template.
projectProject!The workspace the template belongs to.
pages[PortableDocumentPage!]!The rendered pages, in order.
databaseLinks[PortableDocumentDatabaseLink!]!Links that bind the template’s overlay fields to the fields of record tables. Empty when the template has no links.
fileFileThe source file, if still referenced.

PortableDocumentPage

FieldTypeDescription
idID!Unique page identifier.
uidString!Prefixed unique key for the page.
urlString!Storage key of the rendered PNG (the {document-uid}/page_{n} key).
createdAtDateTime!When the page was created.
updatedAtDateTime!When the page was last modified.
documentPortableDocument!The template this page belongs to.
fields[PortableDocumentField!]!Overlay fields placed on this page.

The fields list is populated by createPortableDocumentField; see Map fields and print Portable Documents.

Rename a template

Use the updatePortableDocument mutation to change a template’s name. The only mutable field is name.

mutation RenamePortableDocument {
  updatePortableDocument(input: { id: "pdf_123", name: "Q3 invoice" }) {
    id
    name
  }
}

UpdatePortableDocumentInput

ParameterTypeRequiredDescription
idString!YesThe template to rename.
nameStringNoNew name. A .pdf suffix is appended automatically; omit it to keep the current name.
isActiveBooleanNoSet to false to hide the template from printing and PDF export.
{
  "data": {
    "updatePortableDocument": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "name": "Q3 invoice.pdf"
    }
  }
}

Delete a template

Use the deletePortableDocument mutation to permanently remove a template and its rendered page images from storage. It returns Boolean — true on success — so it takes no sub-selection.

mutation DeletePortableDocument {
  deletePortableDocument(id: "pdf_123")
}
{
  "data": {
    "deletePortableDocument": true
  }
}
ArgumentTypeRequiredDescription
idString!YesThe template to delete.

Fetch one template

Use the portableDocument query to read a single template by ID, including its pages and the overlay fields placed on each page.

query GetPortableDocument {
  portableDocument(id: "pdf_123") {
    id
    name
    createdBy {
      fullName
    }
    pages {
      id
      url
      fields {
        id
        field
        positionX
        positionY
      }
    }
  }
}
ArgumentTypeRequiredDescription
idString!YesThe template to fetch.
{
  "data": {
    "portableDocument": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "name": "Q3 invoice.pdf",
      "createdBy": { "fullName": "Ada Lovelace" },
      "pages": [
        {
          "id": "clm4n8qwx000208l0p1ghi789",
          "url": "pdf_clm4n8qwx000108l0h2abc456/page_1",
          "fields": [
            {
              "id": "clm4n8qwx000508l0f1pqr678",
              "field": "title",
              "positionX": 72,
              "positionY": 540
            }
          ]
        }
      ]
    }
  }
}

List a project’s templates

Use the portableDocuments query to list every template in a workspace, newest-updated first, with offset pagination. It returns a PortableDocumentPagination of { items, pageInfo }.

query ListPortableDocuments {
  portableDocuments(filter: { projectId: "project_123" }, skip: 0, take: 20) {
    items {
      id
      name
      updatedAt
    }
    pageInfo {
      totalItems
      hasNextPage
    }
  }
}

Arguments

ArgumentTypeRequiredDescription
filterPortableDocumentFilter!YesScopes the list to one workspace (below).
skipIntNoItems to skip for offset pagination. Defaults to 0.
takeIntNoItems per page. Schema default is 20; omitting it falls back to 200.

PortableDocumentFilter

FieldTypeRequiredDescription
projectIdString!YesWorkspace to list templates from. This filter is matched by project ID.
{
  "data": {
    "portableDocuments": {
      "items": [
        {
          "id": "clm4n8qwx000008l0g4oxdqn7",
          "name": "Q3 invoice.pdf",
          "updatedAt": "2026-05-29T14:02:11.000Z"
        },
        {
          "id": "clm4n8qwx000608l0g5stu901",
          "name": "NDA.pdf",
          "updatedAt": "2026-05-21T09:18:44.000Z"
        }
      ],
      "pageInfo": {
        "totalItems": 2,
        "hasNextPage": false
      }
    }
  }
}

PageInfo

FieldTypeDescription
totalItemsIntTotal templates matching the filter.
totalPagesIntTotal pages at the current take.
pageIntCurrent page number.
perPageIntItems per page.
hasNextPageBoolean!Whether a further page exists.
hasPreviousPageBoolean!Whether a previous page exists.

Use the setPortableDocumentDatabaseLinks mutation to bind a template’s overlay fields to the fields of record tables (RecordDatabase). The link maps each template placement to a table field, so a batch PDF export of a table’s records can fill the template from each record.

mutation LinkPortableDocument {
  setPortableDocumentDatabaseLinks(
    input: {
      documentId: "pdf_123"
      links: [{ databaseId: "table_123", fieldMappings: { placement_field_1: "field_123" } }]
    }
  ) {
    id
    databaseLinks {
      databaseId
      database {
        name
      }
      fieldMappings
    }
  }
}

The call replaces the template’s links. Pass every link you want to keep; pass an empty links list to remove all of them.

SetPortableDocumentDatabaseLinksInput

ParameterTypeRequiredDescription
documentIdString!YesThe template to link.
links[PortableDocumentDatabaseLinkInput!]!YesThe full replacement set of table links.

PortableDocumentDatabaseLinkInput

FieldTypeRequiredDescription
databaseIdID!YesA record table to link. It must be a live table in the template’s workspace, and one link per table.
fieldMappingsJSON!YesObject mapping a template placement id to a table field id ({ placementId: fieldId }).

Mapping rules

The mapping is validated against the template’s live placements and the table fields you can see:

  • Record-intrinsic placements (workspace, createdBy, completedBy, printedAt, createdAt, updatedAt, completedAt) fill from the record itself and need no mapping.
  • Every other placement needs a mapping to a mounted field visible to you.
  • The mapped field’s type must match the placement. Built-in placements require these field types:
PlacementRequired field type
titleSingle-line text (TEXT_SINGLE)
descriptionMulti-line text (TEXT_MULTI)
assigneesAssignee (ASSIGNEE)
listSingle select (SELECT_SINGLE)
tagsMulti select (SELECT_MULTI)
startedAtDate (DATE)
duedAtDate (DATE)
  • A customField placement is typed by its custom field. If that custom field no longer exists, the mapped field supplies both the identity and the type, and the type must be one the PDF renderer covers (TEXT_SINGLE, TEXT_MULTI, CHECKBOX, DATE, ASSIGNEE, SELECT_SINGLE, SELECT_MULTI, FILE, NUMBER).
  • Mapping keys for placements that no longer exist are dropped.
FieldTypeDescription
databaseIdID!The linked record table’s id.
databaseRecordDatabase!The linked record table.
fieldMappingsJSON!The normalized placement-to-field map in storage.

Errors

CodeWhen
PROJECT_NOT_FOUNDcreatePortableDocument — projectId matches no workspace.
FILE_NOT_FOUNDcreatePortableDocument — fileId can’t be resolved to an uploaded file.
BAD_USER_INPUTcreatePortableDocument — the source is not application/pdf, exceeds 10 pages, or conversion times out at 15s. Also setPortableDocumentDatabaseLinks — any mapping-validation failure.
PORTABLE_DOCUMENT_NOT_FOUNDupdatePortableDocument, deletePortableDocument, portableDocument, or setPortableDocumentDatabaseLinks — no template with that id.
FORBIDDENThe caller is not a member of the template’s workspace (on delete and single-fetch), or lacks contribute permission (on setPortableDocumentDatabaseLinks).

Permissions

All Portable Document operations require an authenticated caller. Beyond authentication:

  • portableDocument and deletePortableDocument additionally require the caller to be a member of the workspace the template belongs to; otherwise they return FORBIDDEN.
  • createPortableDocument resolves the workspace from projectId and attributes the new template to the caller via createdBy.
  • setPortableDocumentDatabaseLinks requires contribute permission on the template’s workspace.