{
  "openapi": "3.0.3",
  "info": {
    "title": "PaperReady Cloud Print API",
    "version": "1.0.0",
    "description": "Print labels from your backend to your users' label printers. Render a saved design server-side and queue it to a paired Print Bridge, send your own pixels, or pass raw ZPL. Metered at $0.01 per delivered print.",
    "contact": {
      "name": "PaperReady",
      "url": "https://paperready.studio/developers/"
    }
  },
  "servers": [
    {
      "url": "https://paperready.studio/api",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    }
  ],
  "tags": [
    {
      "name": "Print",
      "description": "Queue print jobs to a paired Print Bridge"
    },
    {
      "name": "Account",
      "description": "Key identity, printers, templates, jobs"
    },
    {
      "name": "Sub-accounts",
      "description": "Platform embedding: manage child workspaces & their keys, print into them, roll usage up to the parent (requires the subaccounts scope)"
    },
    {
      "name": "Integrations",
      "description": "REST hooks \u2014 subscribe a URL to print events for any no-code tool or your own backend; list recent events"
    }
  ],
  "paths": {
    "/v1/whoami": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Identify the API key",
        "operationId": "whoami",
        "responses": {
          "200": {
            "description": "Key + workspace info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoAmI"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/printers": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "List printers across the workspace's Print Bridges",
        "operationId": "listPrinters",
        "responses": {
          "200": {
            "description": "Printers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "printers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Printer"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "List saved designs + the data fields each expects",
        "operationId": "listTemplates",
        "responses": {
          "200": {
            "description": "Templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/jobs": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Recent print jobs",
        "operationId": "listJobs",
        "responses": {
          "200": {
            "description": "Jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/print": {
      "post": {
        "tags": [
          "Print"
        ],
        "summary": "Queue a single print",
        "operationId": "print",
        "description": "Provide `template` + `data` to render a saved design server-side, OR `data_base64` (a pre-rendered PNG/PDF, or raw ZPL with `format:\"zpl\"`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrintRequest"
              },
              "examples": {
                "template": {
                  "summary": "Render a saved design",
                  "value": {
                    "printer": "Zebra ZD421",
                    "template": "Shipping label",
                    "data": {
                      "name": "Blue Widget",
                      "sku": "PR-0099"
                    }
                  }
                },
                "prerendered": {
                  "summary": "Send your own pixels",
                  "value": {
                    "printer": "Zebra ZD421",
                    "copies": 1,
                    "data_base64": "iVBORw0KGgo..."
                  }
                },
                "zpl": {
                  "summary": "Raw ZPL",
                  "value": {
                    "printer": "Zebra ZD421",
                    "format": "zpl",
                    "data_base64": "XlhBXkZPNTAs..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrintResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "No paired workstation, or an in-flight idempotent duplicate"
          }
        }
      }
    },
    "/v1/print/bulk": {
      "post": {
        "tags": [
          "Print"
        ],
        "summary": "Mail-merge: render a design once per data row",
        "operationId": "printBulk",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkPrintRequest"
              },
              "examples": {
                "bulk": {
                  "value": {
                    "printer": "Zebra ZD421",
                    "template": "Shipping label",
                    "rows": [
                      {
                        "name": "Alice",
                        "sku": "A-1"
                      },
                      {
                        "name": "Bob",
                        "sku": "A-2"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "failed": {
                      "type": "integer"
                    },
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/subaccounts": {
      "get": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "List sub-accounts (child workspaces)",
        "operationId": "listSubaccounts",
        "responses": {
          "200": {
            "description": "Sub-accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subaccounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subaccount"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "post": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "Create (or find) a sub-account",
        "operationId": "createSubaccount",
        "description": "Idempotent on external_id: returns the existing child (created:false) if one already matches.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "external_id": {
                    "type": "string",
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Subaccount"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "created": {
                          "type": "boolean"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "200": {
            "description": "Existing match (created:false)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/subaccounts/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "Get one sub-account",
        "operationId": "getSubaccount",
        "responses": {
          "200": {
            "description": "Sub-account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subaccount"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "delete": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "Delete a sub-account",
        "operationId": "deleteSubaccount",
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/subaccounts/{id}/keys": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "Mint a print key for the sub-account",
        "operationId": "createSubaccountKey",
        "description": "The full key is returned once. Scopes: print, read (scoped to the child).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subaccount_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "key": {
                      "type": "string",
                      "example": "pr_live_\u2026"
                    },
                    "key_prefix": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/subaccounts/{id}/pair-code": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "Get a Print-Bridge pairing code for the sub-account",
        "operationId": "createSubaccountPairCode",
        "responses": {
          "200": {
            "description": "Pairing code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "subaccount_id": {
                      "type": "string"
                    },
                    "expires_in_seconds": {
                      "type": "integer",
                      "example": 1800
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/subaccounts/{id}/usage": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Sub-accounts"
        ],
        "summary": "30-day usage summary for the sub-account",
        "operationId": "getSubaccountUsage",
        "responses": {
          "200": {
            "description": "Usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subaccount_id": {
                      "type": "string"
                    },
                    "window": {
                      "type": "string",
                      "example": "30d"
                    },
                    "prints": {
                      "type": "integer"
                    },
                    "labels": {
                      "type": "integer"
                    },
                    "billable": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/hooks": {
      "get": {
        "tags": [
          "Integrations"
        ],
        "summary": "List REST hooks",
        "operationId": "listHooks",
        "responses": {
          "200": {
            "description": "Hooks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Hook"
                      }
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Integrations"
        ],
        "summary": "Subscribe a URL to print events",
        "operationId": "createHook",
        "description": "Registers target_url to receive signed POSTs when a print changes state (a REST hook for any automation tool or your own service).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "target_url"
                ],
                "properties": {
                  "target_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "print.queued",
                        "print.printed",
                        "print.failed",
                        "print.expired"
                      ]
                    }
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "zapier",
                      "make",
                      "api"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Hook"
                }
              }
            }
          },
          "400": {
            "description": "Invalid target_url"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/hooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Integrations"
        ],
        "summary": "Unsubscribe a REST hook",
        "operationId": "deleteHook",
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Integrations"
        ],
        "summary": "Recent print events (webhook-shaped)",
        "operationId": "listEvents",
        "description": "The last 25 print events in the same shape webhooks deliver \u2014 handy for showing a sample when building a trigger.",
        "responses": {
          "200": {
            "description": "Events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "webhooks": {
    "printEvent": {
      "post": {
        "summary": "Print lifecycle event (configure endpoints in the developer console)",
        "description": "Signed with `X-PaperReady-Signature: t=<ts>,v1=<hmac_sha256(secret, ts + \".\" + rawBody)>`. Types: print.queued, print.printed, print.failed, print.expired.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return 2xx to acknowledge; non-2xx retries with backoff."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer pr_live_..."
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "X-API-Key: pr_live_..."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Dedupes retries \u2014 the same key never prints twice."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ScopeRequired": {
        "description": "The key lacks the subaccounts scope (needs a management key)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "WhoAmI": {
        "type": "object",
        "properties": {
          "workspace_id": {
            "type": "string"
          },
          "key_id": {
            "type": "string"
          }
        }
      },
      "Printer": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "workstation_id": {
            "type": "string"
          },
          "online": {
            "type": "boolean"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size": {
            "type": "object"
          },
          "placeholders": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PrintRequest": {
        "type": "object",
        "required": [
          "printer"
        ],
        "properties": {
          "printer": {
            "type": "string",
            "description": "Target printer name"
          },
          "workstation_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional; defaults to the most-recently-seen Bridge"
          },
          "copies": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 1
          },
          "format": {
            "type": "string",
            "enum": [
              "document",
              "raw",
              "zpl",
              "epl",
              "pdf",
              "png"
            ],
            "default": "document"
          },
          "template": {
            "type": "string",
            "description": "Name/id of a saved design to render server-side"
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values that fill the template's {{placeholders}}"
          },
          "data_base64": {
            "type": "string",
            "description": "A pre-rendered PNG/PDF, or raw ZPL bytes when format=zpl"
          },
          "subaccount_id": {
            "type": "string",
            "format": "uuid",
            "description": "Management key only: print into this child workspace (usage records under the child, billed to the parent). Template lookup falls back to the parent library."
          }
        }
      },
      "PrintResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "usage_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "printer": {
            "type": "string"
          },
          "copies": {
            "type": "integer"
          },
          "format": {
            "type": "string"
          },
          "template": {
            "type": "string",
            "nullable": true
          },
          "billed": {
            "type": "boolean"
          },
          "target": {
            "type": "object"
          },
          "expires_at": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "BulkPrintRequest": {
        "type": "object",
        "required": [
          "printer",
          "template",
          "rows"
        ],
        "properties": {
          "printer": {
            "type": "string"
          },
          "workstation_id": {
            "type": "string",
            "format": "uuid"
          },
          "template": {
            "type": "string"
          },
          "copies": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "maxItems": 500
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "example": "print.printed"
          },
          "created": {
            "type": "integer"
          },
          "data": {
            "type": "object"
          }
        }
      },
      "Subaccount": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": "string",
            "nullable": true,
            "description": "Your own tenant id; the create call is idempotent on it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "prints_30d": {
            "type": "integer"
          }
        }
      },
      "Hook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "target_url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty = all events"
          },
          "source": {
            "type": "string",
            "enum": [
              "zapier",
              "make",
              "api"
            ]
          },
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_9f2c1a"
          },
          "type": {
            "type": "string",
            "enum": [
              "print.queued",
              "print.printed",
              "print.failed",
              "print.expired"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Unix seconds"
          },
          "data": {
            "type": "object",
            "properties": {
              "job_id": {
                "type": "string"
              },
              "printer": {
                "type": "string"
              },
              "copies": {
                "type": "integer"
              },
              "status": {
                "type": "string"
              },
              "template": {
                "type": "string",
                "nullable": true
              },
              "workstation_id": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      }
    }
  }
}