{"info":{"_postman_id":"6fb01419-f1c3-4a26-9a02-d93244d40081","name":"Doqlo Bulk Fill Public API","description":"<html><head></head><body><p>Use this collection to run the Doqlo Bulk Fill Public API locally in Postman.</p>\n<p>Doqlo Bulk Fill Public API executes <code>.doqlo</code> packages exported from the web Bulk Fill editor.</p>\n<p>Before you run the requests:</p>\n<ul>\n<li>create the layout and mapping in the web app first</li>\n<li>export the <code>.doqlo</code> package from the web app</li>\n<li>create a Bulk Fill API key from your account page</li>\n<li>set <code>DOQLO_API_KEY</code> in the imported Postman environment</li>\n<li>choose local files for <code>pdf</code> and <code>doqlo_file</code></li>\n<li>submit a job</li>\n<li>poll the job if needed</li>\n<li>download the ZIP result when the job is completed</li>\n</ul>\n<p>Free plan users can run the Public API within Free plan limits. Paid plans support larger PDFs and higher monthly volume.</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"54295397","collectionId":"6fb01419-f1c3-4a26-9a02-d93244d40081","publishedId":"2sBXqKneZN","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-04-30T19:48:53.000Z"},"item":[{"name":"Create export job with JSON rows","event":[{"listen":"test","script":{"type":"text/javascript","exec":["let data = null;","try {","  data = pm.response.json();","} catch (error) {","  data = null;","}","if (data && typeof data.job_id === 'string' && pm.environment && typeof pm.environment.set === 'function') {","  pm.environment.set('DOQLO_JOB_ID', data.job_id);","}"],"id":"ebc6982b-4d9f-4bcc-ad0b-f9de6840eb72"}}],"id":"e884e6d7-7ee0-4f5b-b33e-57db7ae7786a","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Idempotency-Key","value":"{{DOQLO_IDEMPOTENCY_KEY}}","type":"text"},{"key":"X-Request-Id","value":"{{DOQLO_REQUEST_ID}}","type":"text"}],"body":{"mode":"formdata","formdata":[{"key":"pdf","type":"file","description":"<p>Choose <code>source.pdf</code> locally after import. Postman collections do not bundle local files.</p>\n","value":null},{"key":"doqlo_file","type":"file","description":"<p>Choose <code>package.doqlo</code> locally after import. Postman collections do not bundle local files.</p>\n","value":null},{"key":"max_failed_row_percent","value":"5","type":"text"},{"key":"rows_json","value":"[{\"column_0\":\"Alice Nguyen\",\"column_1\":\"INV-1001\"},{\"column_0\":\"Marco Silva\",\"column_1\":\"INV-1002\"}]","type":"text"},{"key":"filename_columns","value":"[\"column_0\",\"column_1\"]","type":"text"},{"key":"flatten_forms","value":"false","type":"text"},{"key":"include_stickers","value":"false","type":"text"}]},"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs","description":"<p>Create a minimal response-mode export job.</p>\n<ul>\n<li>Choose local <code>pdf</code> and <code>doqlo_file</code> files after import because Postman collections do not bundle local files.</li>\n<li><code>rows_json</code> uses positional keys such as <code>column_0</code> and <code>column_1</code>.</li>\n<li>If the response includes a top-level <code>job_id</code>, this request saves it to <code>DOQLO_JOB_ID</code> in the active environment.</li>\n<li>Use a new <code>DOQLO_IDEMPOTENCY_KEY</code> value for each new logical create request. Change it before switching between JSON and CSV create examples.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"{{DOQLO_API_KEY}}"}]},"isInherited":true,"source":{"_postman_id":"6fb01419-f1c3-4a26-9a02-d93244d40081","id":"6fb01419-f1c3-4a26-9a02-d93244d40081","name":"Doqlo Bulk Fill Public API","type":"collection"}},"urlObject":{"path":["v1","bulkfill","export-jobs"],"host":["{{DOQLO_BASE_URL}}"],"query":[],"variable":[]}},"response":[{"id":"96d129e7-e279-47ff-8183-9566a548c518","name":"200 Completed export job","originalRequest":{"method":"POST","header":[{"key":"Idempotency-Key","value":"{{DOQLO_IDEMPOTENCY_KEY}}","type":"text"},{"key":"X-Request-Id","value":"{{DOQLO_REQUEST_ID}}","type":"text"}],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"cookie":[],"responseTime":null,"body":"{\n  \"job_id\": \"example-job-id\",\n  \"status\": \"completed\",\n  \"created_at\": \"2026-04-24T15:18:30.577005Z\",\n  \"started_at\": \"2026-04-24T15:18:30.592607Z\",\n  \"completed_at\": \"2026-04-24T15:18:32.836613Z\",\n  \"result\": {\n    \"delivery_mode\": \"direct\",\n    \"download_url\": \"/v1/bulkfill/export-jobs/example-job-id/download\",\n    \"expires_at\": \"2026-04-24T21:18:32.824000Z\",\n    \"file_size_bytes\": 2921485\n  }\n}"}],"_postman_id":"e884e6d7-7ee0-4f5b-b33e-57db7ae7786a"},{"name":"Create export job with CSV file","event":[{"listen":"test","script":{"type":"text/javascript","exec":["let data = null;","try {","  data = pm.response.json();","} catch (error) {","  data = null;","}","if (data && typeof data.job_id === 'string' && pm.environment && typeof pm.environment.set === 'function') {","  pm.environment.set('DOQLO_JOB_ID', data.job_id);","}"],"id":"83b23b1c-499d-4db0-b104-479cbc929595"}}],"id":"09de2e3b-4f96-4d50-84d3-b1aa2484d959","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Idempotency-Key","value":"{{DOQLO_IDEMPOTENCY_KEY}}","type":"text"},{"key":"X-Request-Id","value":"{{DOQLO_REQUEST_ID}}","type":"text"}],"body":{"mode":"formdata","formdata":[{"key":"pdf","type":"file","description":"<p>Choose <code>source.pdf</code> locally after import. Postman collections do not bundle local files.</p>\n","value":null},{"key":"doqlo_file","type":"file","description":"<p>Choose <code>package.doqlo</code> locally after import. Postman collections do not bundle local files.</p>\n","value":null},{"key":"csv_file","type":"file","description":"<p>Choose <code>rows.csv</code> locally after import. The public CSV contract is positional and does not auto-detect headers.</p>\n","value":null},{"key":"max_failed_row_percent","value":"5","type":"text"},{"key":"filename_columns","value":"[\"column_0\",\"column_1\"]","type":"text"},{"key":"flatten_forms","value":"false","type":"text"},{"key":"include_stickers","value":"false","type":"text"}]},"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs","description":"<p>Create a response-mode export job from a CSV upload.</p>\n<ul>\n<li>Choose local <code>pdf</code>, <code>doqlo_file</code>, and <code>csv_file</code> files after import because Postman collections do not bundle local files.</li>\n<li>Provide exactly one of <code>rows_json</code> or <code>csv_file</code>.</li>\n<li>The public CSV contract is positional. Keep your mapped columns aligned to <code>column_0</code>, <code>column_1</code>, and later positions.</li>\n<li>If the response includes a top-level <code>job_id</code>, this request saves it to <code>DOQLO_JOB_ID</code> in the active environment.</li>\n<li>Use a new <code>DOQLO_IDEMPOTENCY_KEY</code> value for each new logical create request. Change it before switching between JSON and CSV create examples.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"{{DOQLO_API_KEY}}"}]},"isInherited":true,"source":{"_postman_id":"6fb01419-f1c3-4a26-9a02-d93244d40081","id":"6fb01419-f1c3-4a26-9a02-d93244d40081","name":"Doqlo Bulk Fill Public API","type":"collection"}},"urlObject":{"path":["v1","bulkfill","export-jobs"],"host":["{{DOQLO_BASE_URL}}"],"query":[],"variable":[]}},"response":[{"id":"d5c68590-438a-4459-80b7-e32d8d75e40f","name":"200 Completed export job","originalRequest":{"method":"POST","header":[{"key":"Idempotency-Key","value":"{{DOQLO_IDEMPOTENCY_KEY}}","type":"text"},{"key":"X-Request-Id","value":"{{DOQLO_REQUEST_ID}}","type":"text"}],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"cookie":[],"responseTime":null,"body":"{\n  \"job_id\": \"example-job-id\",\n  \"status\": \"completed\",\n  \"created_at\": \"2026-04-24T15:18:30.577005Z\",\n  \"started_at\": \"2026-04-24T15:18:30.592607Z\",\n  \"completed_at\": \"2026-04-24T15:18:32.836613Z\",\n  \"result\": {\n    \"delivery_mode\": \"direct\",\n    \"download_url\": \"/v1/bulkfill/export-jobs/example-job-id/download\",\n    \"expires_at\": \"2026-04-24T21:18:32.824000Z\",\n    \"file_size_bytes\": 2921485\n  }\n}"}],"_postman_id":"09de2e3b-4f96-4d50-84d3-b1aa2484d959"},{"name":"Get export job status","id":"4f32c6f4-b434-45c8-83cd-8055845911de","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs/{{DOQLO_JOB_ID}}","description":"<p>Read the current state of one export job.</p>\n<ul>\n<li>Use this request if create returns <code>queued</code> or <code>processing</code>.</li>\n<li>Wait until status is <code>completed</code> before downloading.</li>\n<li>If status is <code>failed</code>, inspect the returned error details.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"{{DOQLO_API_KEY}}"}]},"isInherited":true,"source":{"_postman_id":"6fb01419-f1c3-4a26-9a02-d93244d40081","id":"6fb01419-f1c3-4a26-9a02-d93244d40081","name":"Doqlo Bulk Fill Public API","type":"collection"}},"urlObject":{"path":["v1","bulkfill","export-jobs","{{DOQLO_JOB_ID}}"],"host":["{{DOQLO_BASE_URL}}"],"query":[],"variable":[]}},"response":[{"id":"65528390-a532-4295-a5b6-226ffe7ef99c","name":"200 Completed export job status","originalRequest":{"method":"GET","header":[],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs/{{DOQLO_JOB_ID}}"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"cookie":[],"responseTime":null,"body":"{\n  \"job_id\": \"example-job-id\",\n  \"status\": \"completed\",\n  \"created_at\": \"2026-04-24T15:18:30.577005Z\",\n  \"started_at\": \"2026-04-24T15:18:30.592607Z\",\n  \"completed_at\": \"2026-04-24T15:18:32.836613Z\",\n  \"result\": {\n    \"delivery_mode\": \"direct\",\n    \"download_url\": \"/v1/bulkfill/export-jobs/example-job-id/download\",\n    \"expires_at\": \"2026-04-24T21:18:32.824000Z\",\n    \"file_size_bytes\": 2921485\n  }\n}"}],"_postman_id":"4f32c6f4-b434-45c8-83cd-8055845911de"},{"name":"Download export result","event":[{"listen":"test","script":{"type":"text/javascript","exec":["// Proof-of-download visualization for ZIP file","const stream = pm.response.stream;","const bytes = new Uint8Array(stream);","const totalSize = bytes.length;","","// 1. Check ZIP magic signature: PK\\x03\\x04","const isZip = bytes.length >= 4 &&","  bytes[0] === 0x50 && bytes[1] === 0x4B &&","  bytes[2] === 0x03 && bytes[3] === 0x04;","","const magicHex = Array.from(bytes.slice(0, 4))","  .map((b) => b.toString(16).padStart(2, '0').toUpperCase())","  .join(' ');","","// 2. Format size","function formatSize(n) {","  if (n < 1024) return n + ' B';","  if (n < 1024 * 1024) return (n / 1024).toFixed(2) + ' KB';","  return (n / (1024 * 1024)).toFixed(2) + ' MB';","}","","// 3. Parse ZIP central directory to list files inside","function parseZipFileList(buf) {","  const files = [];","  let eocdOffset = -1;","  const maxScan = Math.min(buf.length, 65557);","","  for (let i = buf.length - 22; i >= buf.length - maxScan && i >= 0; i--) {","    if (buf[i] === 0x50 && buf[i + 1] === 0x4B && buf[i + 2] === 0x05 && buf[i + 3] === 0x06) {","      eocdOffset = i;","      break;","    }","  }","","  if (eocdOffset < 0) return files;","","  const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);","  const totalEntries = dv.getUint16(eocdOffset + 10, true);","  const cdOffset = dv.getUint32(eocdOffset + 16, true);","","  let p = cdOffset;","  for (let i = 0; i < totalEntries && p + 46 <= buf.length; i++) {","    if (!(buf[p] === 0x50 && buf[p + 1] === 0x4B && buf[p + 2] === 0x01 && buf[p + 3] === 0x02)) break;","","    const compressedSize = dv.getUint32(p + 20, true);","    const uncompressedSize = dv.getUint32(p + 24, true);","    const fileNameLen = dv.getUint16(p + 28, true);","    const extraLen = dv.getUint16(p + 30, true);","    const commentLen = dv.getUint16(p + 32, true);","","    const nameBytes = buf.slice(p + 46, p + 46 + fileNameLen);","    const name = new TextDecoder('utf-8').decode(nameBytes);","","    files.push({","      idx: i + 1,","      name: name,","      compressedFmt: formatSize(compressedSize),","      uncompressedFmt: formatSize(uncompressedSize)","    });","","    p += 46 + fileNameLen + extraLen + commentLen;","  }","","  return files;","}","","const files = isZip ? parseZipFileList(bytes) : [];","const contentType = pm.response.headers.get('Content-Type') || 'n/a';","const contentDisposition = pm.response.headers.get('Content-Disposition') || 'n/a';","","pm.test('Status code is 200', () => pm.response.to.have.status(200));","pm.test('Response is a valid ZIP file (PK header)', () => {","  pm.expect(isZip).to.be.true;","});","pm.test('ZIP contains at least one file', () => {","  pm.expect(files.length).to.be.above(0);","});","","const template = `","<style>","  body { font-family: -apple-system, Segoe UI, Roboto, sans-serif; padding: 20px; color: #222; background: #fafbfc; }","  .card { border: 1px solid #e1e4e8; border-radius: 8px; padding: 20px; margin-bottom: 16px; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,0.04); }","  .header { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }","  .badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-weight: 600; font-size: 12px; letter-spacing: 0.3px; }","  .badge-ok { background: #d4f4dd; color: #0a7a2f; }","  .badge-fail { background: #fde2e2; color: #b42318; }","  h2 { margin: 0; font-size: 18px; }","  .check { color: #0a7a2f; font-weight: 700; margin-right: 6px; }","  .fail { color: #b42318; font-weight: 700; margin-right: 6px; }","  table { width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 13px; }","  th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eef0f2; }","  th { background: #f6f8fa; font-weight: 600; }","  .meta { display: grid; grid-template-columns: 220px 1fr; gap: 8px 16px; font-size: 13px; }","  .meta div:nth-child(odd) { color: #666; font-weight: 600; }","  .mono { font-family: ui-monospace, Menlo, Consolas, monospace; }","</style>","","<div class=\"card\">","  <div class=\"header\">","    <h2>ZIP Download Verification</h2>","    {{#if isZip}}<span class=\"badge badge-ok\">VALID ZIP</span>{{else}}<span class=\"badge badge-fail\">INVALID</span>{{/if}}","  </div>","","  <div class=\"meta\">","    <div>ZIP Signature</div>","    <div>","      {{#if isZip}}<span class=\"check\">&#10003;</span>{{else}}<span class=\"fail\">&#10007;</span>{{/if}}","      <span class=\"mono\">{{magicHex}}</span> <span style=\"color:#888\">(expected 50 4B 03 04)</span>","    </div>","    <div>Total Size</div>","    <div>{{sizeFmt}} <span class=\"mono\" style=\"color:#888\">({{totalSize}} bytes)</span></div>","    <div>Content-Type</div>","    <div class=\"mono\">{{contentType}}</div>","    <div>Content-Disposition</div>","    <div class=\"mono\">{{contentDisposition}}</div>","    <div>Files in Archive</div>","    <div><strong>{{fileCount}}</strong></div>","  </div>","</div>","","{{#if fileCount}}","<div class=\"card\">","  <h2 style=\"margin-top:0\">Archive Contents</h2>","  <table>","    <thead>","      <tr>","        <th style=\"width:40px\">#</th>","        <th>File Name</th>","        <th style=\"width:120px\">Compressed</th>","        <th style=\"width:120px\">Uncompressed</th>","      </tr>","    </thead>","    <tbody>","      {{#each files}}","      <tr>","        <td>{{idx}}</td>","        <td class=\"mono\">{{name}}</td>","        <td>{{compressedFmt}}</td>","        <td>{{uncompressedFmt}}</td>","      </tr>","      {{/each}}","    </tbody>","  </table>","</div>","{{/if}}","`;","","const visualizerData = {","  isZip: isZip,","  magicHex: magicHex,","  totalSize: totalSize,","  sizeFmt: formatSize(totalSize),","  contentType: contentType,","  contentDisposition: contentDisposition,","  fileCount: files.length,","  files: files","};","","if (pm.visualizer && typeof pm.visualizer.set === 'function') {","  pm.visualizer.set(template, visualizerData);","}"],"id":"5771393b-6fca-4aa9-b6a1-82717259780f"}}],"id":"948f2ab2-1d58-4c1b-87fb-11e2bc25e1bc","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs/{{DOQLO_JOB_ID}}/download","description":"<p>Download the completed ZIP result.</p>\n<ul>\n<li>Run this request after the job status is <code>completed</code>.</li>\n<li>The response is an <code>application/zip</code> download.</li>\n<li>In Postman, use <code>Send and Download</code> to save the ZIP file.</li>\n<li>The ZIP contains generated row PDFs and <code>manifest.json</code>.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"{{DOQLO_API_KEY}}"}]},"isInherited":true,"source":{"_postman_id":"6fb01419-f1c3-4a26-9a02-d93244d40081","id":"6fb01419-f1c3-4a26-9a02-d93244d40081","name":"Doqlo Bulk Fill Public API","type":"collection"}},"urlObject":{"path":["v1","bulkfill","export-jobs","{{DOQLO_JOB_ID}}","download"],"host":["{{DOQLO_BASE_URL}}"],"query":[],"variable":[]}},"response":[{"id":"52e8a65b-9f8a-4bf1-a29b-6ea39d255146","name":"200 ZIP download","originalRequest":{"method":"GET","header":[],"url":"{{DOQLO_BASE_URL}}/v1/bulkfill/export-jobs/{{DOQLO_JOB_ID}}/download"},"status":"OK","code":200,"_postman_previewlanguage":"text","header":[{"key":"Content-Type","value":"application/zip"},{"key":"Content-Disposition","value":"attachment; filename=\"bulk_export_example.zip\""}],"cookie":[],"responseTime":null,"body":"Binary ZIP response. Use Send and Download in Postman. The ZIP contains generated row PDFs and manifest.json."}],"_postman_id":"948f2ab2-1d58-4c1b-87fb-11e2bc25e1bc"}],"auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"{{DOQLO_API_KEY}}"}]}}}