Map fields and print Portable Documents

Place overlay fields that bind a record's attributes onto a Portable Document page, then print a filled PDF for any record.


A Portable Document template is only useful once you tell it where each piece of record data should appear. Use the createPortableDocumentField mutation to place an overlay field on a template page: each field binds one of a record’s attributes — its title, description, due date, assignees, tags, or a specific custom field — to an x/y position with an optional width and height. Then call printPortableDocument to render a filled PDF for any specific record and get back its storage key.

Records are Todo objects in the API, and workspaces are Project objects. Overlay fields are PortableDocumentField objects, each attached to a PortableDocumentPage of a PortableDocument template. Placing, moving, and deleting fields requires contribute permission on the template’s workspace.

Create a field

Use createPortableDocumentField to place a field on a page. The smallest call binds a record attribute (field) to a position (positionX / positionY) on a page (pageId).

mutation PlaceTitleField {
  createPortableDocumentField(
    input: { pageId: "page_123", field: "title", positionX: 120.0, positionY: 64.0 }
  ) {
    id
    field
    positionX
    positionY
  }
}

To bind a custom field, set field: "customField" and supply the customFieldId — omitting it raises CUSTOM_FIELD_NOT_FOUND. You can also pass width and height to define a bounding box (text wraps inside it; without bounds the value is drawn on a single line at the anchor point).

mutation PlaceCustomFieldBox {
  createPortableDocumentField(
    input: {
      pageId: "page_123"
      field: "customField"
      customFieldId: "field_123"
      positionX: 120.0
      positionY: 220.0
      width: 240.0
      height: 48.0
    }
  ) {
    id
    field
    width
    height
    customField {
      id
      name
    }
  }
}

CreatePortableDocumentFieldInput

ParameterTypeRequiredDescription
pageIdString!YesThe PortableDocumentPage this field is placed on. The page’s parent template determines which workspace’s permissions apply.
fieldString!YesWhich record attribute to bind. One of title, description, dueEnd, assignees, tags, or customField.
positionXFloat!YesHorizontal anchor of the field on the page.
positionYFloat!YesVertical anchor of the field on the page.
widthFloatNoBounding-box width. When set together with height, the printed value wraps to fit.
heightFloatNoBounding-box height.
customFieldIdStringNoRequired when field is customField — the CustomField to read for each record. Ignored for other field values.
idStringNoPass an existing field id to upsert (reuse) that id; omit it to create a new field.

field values

ValueBinds to the record’s
titleTitle (Todo.title).
descriptionDescription text (Todo.text).
dueEndDue date (Todo.duedAt), formatted in the record’s timezone.
assigneesAssignee names, comma-separated.
tagsTag titles, comma-separated.
customFieldThe custom field named by customFieldId, formatted by its type.

Response

{
  "data": {
    "createPortableDocumentField": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "field": "title",
      "positionX": 120.0,
      "positionY": 64.0
    }
  }
}

PortableDocumentField

FieldTypeDescription
idID!Unique identifier of the field.
uidString!Short opaque identifier.
fieldString!The bound attribute (title, description, dueEnd, assignees, tags, or customField).
positionXFloat!Horizontal anchor on the page.
positionYFloat!Vertical anchor on the page.
widthFloatBounding-box width, if set.
heightFloatBounding-box height, if set.
createdAtDateTime!When the field was created.
updatedAtDateTime!When the field was last changed.
pagePortableDocumentPage!The page the field is placed on.
customFieldCustomFieldThe bound custom field, when field is customField; otherwise null.

Update a field

Use updatePortableDocumentField to reposition or resize an existing field, or to move it to a different page with pageId. positionX and positionY are required on every update; width, height, and pageId are optional.

mutation MoveField {
  updatePortableDocumentField(
    input: { id: "field_123", positionX: 96.0, positionY: 300.0, width: 200.0, height: 40.0 }
  ) {
    id
    positionX
    positionY
    width
    height
  }
}

UpdatePortableDocumentFieldInput

ParameterTypeRequiredDescription
idString!YesThe PortableDocumentField to update.
positionXFloat!YesNew horizontal anchor.
positionYFloat!YesNew vertical anchor.
widthFloatNoNew bounding-box width.
heightFloatNoNew bounding-box height.
pageIdStringNoMove the field to a different page. Omit to keep it on its current page.

updatePortableDocumentField returns the updated PortableDocumentField. The bound attribute (field) and any customField are fixed at creation — to change what a field binds to, delete it and create a new one.

Delete a field

Use deletePortableDocumentField to remove a field by id. It returns Boolean (true on success), so it takes no sub-selection.

mutation RemoveField {
  deletePortableDocumentField(id: "field_123")
}
{
  "data": {
    "deletePortableDocumentField": true
  }
}

Use printPortableDocument to render a filled PDF: it takes the template id (pdfId) and a record id (todoId), draws every overlay field’s value for that record onto the template’s pages, and returns the new file’s storage key.

mutation PrintRecord {
  printPortableDocument(pdfId: "pdf_123", todoId: "todo_123")
}
{
  "data": {
    "printPortableDocument": "clm4n8qwx000008l0g4oxdqn7/acme-invoice.pdf"
  }
}
The return value is a storage key, not a URL

printPortableDocument returns a legacy storage key string of the form {file.uid}/{file.name} — not a downloadable URL — or null if no file was produced. Resolve it through Blue’s file APIs to fetch the rendered PDF. See Upload files.

Errors

createPortableDocumentField

CodeWhen
PORTABLE_DOCUMENT_NOT_FOUNDNo template owns the given pageId.
CUSTOM_FIELD_NOT_FOUNDfield is customField but customFieldId is missing or doesn’t resolve to a custom field.
FORBIDDENThe caller lacks contribute permission on the template’s workspace.
UNAUTHENTICATEDNo valid credentials on the request.

updatePortableDocumentField / deletePortableDocumentField

CodeWhen
PORTABLE_DOCUMENT_FIELD_NOT_FOUNDNo field matches the given id.
FORBIDDEN(delete) The caller lacks contribute permission on the template’s workspace.
UNAUTHENTICATEDNo valid credentials on the request.

printPortableDocument

CodeWhen
TODO_NOT_FOUNDNo record matches todoId.
PORTABLE_DOCUMENT_NOT_FOUNDNo template matches pdfId.
UNAUTHENTICATEDNo valid credentials on the request.

Permissions

createPortableDocumentField and deletePortableDocumentField require contribute permission on the workspace that owns the field’s template — the same level needed to edit records there. updatePortableDocumentField and printPortableDocument require an authenticated caller. All four are scoped by the X-Bloo-Company-ID and X-Bloo-Project-ID headers (or an ID/slug in the input). Header names are case-insensitive.