{
 "info": {
  "_postman_id": "f0015cab-1e60-4a00-9000-a91de1e6aca0",
  "name": "Foolscap — API de integração",
  "description": "A API pública — o que o dev do cliente usa com a key de integração (fsk_). Gerar documentos, descobrir templates, consultar histórico e cota.\n\nAntes de chamar: crie uma API key no painel (dev.foolscap.app.br → API Keys) e preencha a variável `api_key` da collection com o segredo fsk_.\n\nGerada a partir da collection completa do repositório (api/postman/) — não editar à mão.",
  "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
 },
 "auth": {
  "type": "bearer",
  "bearer": [
   {
    "key": "token",
    "value": "{{api_key}}",
    "type": "string"
   }
  ]
 },
 "variable": [
  {
   "key": "baseUrl",
   "value": "https://api.dev.foolscap.app.br",
   "type": "string"
  },
  {
   "key": "api_key",
   "value": "",
   "type": "string"
  },
  {
   "key": "template_id",
   "value": "",
   "type": "string"
  },
  {
   "key": "document_id",
   "value": "",
   "type": "string"
  },
  {
   "key": "pdf_url",
   "value": "",
   "type": "string"
  }
 ],
 "item": [
  {
   "name": "Health",
   "request": {
    "method": "GET",
    "url": {
     "raw": "{{baseUrl}}/health",
     "host": [
      "{{baseUrl}}"
     ],
     "path": [
      "health"
     ]
    },
    "description": "Verifica se a API está no ar."
   },
   "event": [
    {
     "listen": "test",
     "script": {
      "type": "text/javascript",
      "exec": [
       "pm.test('status 200', () => pm.response.to.have.status(200));",
       "pm.test('status ok', () => pm.expect(pm.response.json().status).to.eql('ok'));"
      ]
     }
    }
   ]
  },
  {
   "name": "Render",
   "description": "Geração de PDF. Os requests de sucesso salvam pdf_url e document_id na collection.",
   "item": [
    {
     "name": "Render — template + JSON",
     "request": {
      "method": "POST",
      "url": {
       "raw": "{{baseUrl}}/v1/render",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "render"
       ]
      },
      "header": [
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"html\": \"<style>body{font-family:sans-serif;margin:3em} h1{border-bottom:2px solid #333}</style><h1>Contrato de Prestação de Serviços</h1><p>Contratante: <b>{{ contratante.nome }}</b>, CPF {{ contratante.cpf }}</p><p>Valor mensal: R$ {{ valor }}</p><ul>{% for s in servicos %}<li>{{ s }}</li>{% endfor %}</ul>\",\n  \"data\": {\n    \"contratante\": { \"nome\": \"Maria Silva\", \"cpf\": \"123.456.789-00\" },\n    \"valor\": \"1.500,00\",\n    \"servicos\": [\"Desenvolvimento\", \"Manutenção\", \"Suporte\"]\n  }\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      },
      "description": "Com `data` presente, o `html` é tratado como template Jinja2. Campo ausente no `data` → 422 com mensagem clara. A geração é assíncrona (202) — acompanhe com \"Detalhar documento\" até o status sair de `processando`."
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 202 (geração assíncrona)', () => pm.response.to.have.status(202));",
         "const body = pm.response.json();",
         "pm.test('retorna document_id', () => pm.expect(body.document_id).to.be.a('string'));",
         "pm.test('status processando', () => pm.expect(body.status).to.eql('processando'));",
         "pm.collectionVariables.set('document_id', body.document_id);"
        ]
       }
      }
     ]
    },
    {
     "name": "Render — HTML pronto (sem data)",
     "request": {
      "method": "POST",
      "url": {
       "raw": "{{baseUrl}}/v1/render",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "render"
       ]
      },
      "header": [
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"html\": \"<h1>Recibo</h1><p>Documento estático, sem template.</p>\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      },
      "description": "Sem `data`, o `html` é renderizado como está (não passa pelo Jinja2). Resposta 202 — acompanhe em \"Detalhar documento\"."
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 202 (geração assíncrona)', () => pm.response.to.have.status(202));",
         "pm.collectionVariables.set('document_id', pm.response.json().document_id);"
        ]
       }
      }
     ]
    },
    {
     "name": "Render — por template_id",
     "request": {
      "method": "POST",
      "url": {
       "raw": "{{baseUrl}}/v1/render",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "render"
       ]
      },
      "description": "Renderiza um template salvo com dados de produção. Use `template_version` para fixar uma versão antiga; sem ele, usa a publicada. O document_name opcional dá um nome amigável ao registro no histórico. Resposta 202 — acompanhe em \"Detalhar documento\".",
      "header": [
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"template_id\": \"{{template_id}}\",\n  \"data\": {\n    \"contratante\": {\n      \"nome\": \"João Souza\",\n      \"cpf\": \"987.654.321-00\"\n    },\n    \"servicos\": [\n      \"Consultoria\"\n    ]\n  },\n  \"document_name\": \"Contrato — João Souza\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 202 (geração assíncrona)', () => pm.response.to.have.status(202));",
         "pm.collectionVariables.set('document_id', pm.response.json().document_id);"
        ]
       }
      }
     ]
    },
    {
     "name": "Render — erro de template (422)",
     "request": {
      "method": "POST",
      "url": {
       "raw": "{{baseUrl}}/v1/render",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "render"
       ]
      },
      "header": [
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"html\": \"<p>{{ cliente.nome }}</p>\",\n  \"data\": {}\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      },
      "description": "Campo usado no template mas ausente no `data`: a API responde 422 com a variável faltante no `detail` — nunca PDF com buraco."
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 422', () => pm.response.to.have.status(422));",
         "pm.test('detail cita a variável', () => pm.expect(pm.response.json().detail).to.include('cliente'));"
        ]
       }
      }
     ]
    }
   ]
  },
  {
   "name": "Listar templates",
   "request": {
    "method": "GET",
    "url": {
     "raw": "{{baseUrl}}/v1/templates",
     "host": [
      "{{baseUrl}}"
     ],
     "path": [
      "v1",
      "templates"
     ]
    },
    "description": "Lista templates ativos (arquivados não aparecem)."
   },
   "event": [
    {
     "listen": "test",
     "script": {
      "type": "text/javascript",
      "exec": [
       "pm.test('status 200', () => pm.response.to.have.status(200));"
      ]
     }
    }
   ]
  },
  {
   "name": "Listar modelos da galeria",
   "request": {
    "method": "GET",
    "url": {
     "raw": "{{baseUrl}}/v1/gallery",
     "host": [
      "{{baseUrl}}"
     ],
     "path": [
      "v1",
      "gallery"
     ]
    },
    "description": "Catálogo de modelos prontos mantidos pelo Foolscap (contrato, proposta, recibo, RH, escola, NDA). Vem sem o HTML — o `sample_data` de cada modelo já mostra o contrato de dados do render. Para usar um modelo, instale-o na conta pelo painel: ele vira um template normal, com id próprio, e passa a aparecer em 'Listar templates'."
   },
   "event": [
    {
     "listen": "test",
     "script": {
      "type": "text/javascript",
      "exec": [
       "pm.test('status 200', () => pm.response.to.have.status(200));",
       "const body = pm.response.json();",
       "pm.test('traz os modelos com contrato de dados', () => {",
       "  pm.expect(body.length).to.be.above(0);",
       "  pm.expect(body[0]).to.have.property('slug');",
       "  pm.expect(body[0]).to.have.property('sample_data');",
       "});"
      ]
     }
    }
   ]
  },
  {
   "name": "Documentos",
   "description": "Histórico dos documentos gerados (paginado) e re-download por URL nova.",
   "item": [
    {
     "name": "Listar documentos",
     "request": {
      "method": "GET",
      "url": {
       "raw": "{{baseUrl}}/v1/documents",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "documents"
       ],
       "query": [
        {
         "key": "limit",
         "value": "50",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "",
         "disabled": true
        }
       ]
      },
      "description": "Paginado: resposta { items, next_cursor }. Mais recentes primeiro. ?limit=1..200 (padrão 50); para a próxima página, repita com ?cursor=<next_cursor>. Cada item traz origin ('painel' | 'api') e origin_label (label da key, quando origin=api) — nulos em documentos antigos."
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 200', () => pm.response.to.have.status(200));"
        ]
       }
      }
     ]
    },
    {
     "name": "Detalhar documento (URL nova)",
     "request": {
      "method": "GET",
      "url": {
       "raw": "{{baseUrl}}/v1/documents/{{document_id}}",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "v1",
        "documents",
        "{{document_id}}"
       ]
      },
      "description": "O ponto de conclusão do render assíncrono — repita até `status` sair de `processando`. Devolve os metadados + uma pdf_url recém-assinada quando `gerado` (é assim que o cliente também recupera um documento antigo)."
     },
     "event": [
      {
       "listen": "test",
       "script": {
        "type": "text/javascript",
        "exec": [
         "pm.test('status 200', () => pm.response.to.have.status(200));",
         "const doc = pm.response.json();",
         "pm.test('não falhou', () => pm.expect(doc.status).to.not.eql('falhou'));",
         "if (doc.status === 'gerado') pm.collectionVariables.set('pdf_url', doc.pdf_url);",
         "else console.log('ainda processando — rode de novo');"
        ]
       }
      }
     ]
    }
   ]
  },
  {
   "name": "Minha conta + uso do mês",
   "request": {
    "method": "GET",
    "url": {
     "raw": "{{baseUrl}}/v1/account",
     "host": [
      "{{baseUrl}}"
     ],
     "path": [
      "v1",
      "account"
     ]
    },
    "description": "Dados da conta autenticada e quantos documentos foram gerados no mês (previews não contam). Traz também o aceite dos termos no cadastro (terms_accepted_at + terms_version; nulos em contas anteriores ao registro)."
   },
   "event": [
    {
     "listen": "test",
     "script": {
      "type": "text/javascript",
      "exec": [
       "pm.test('status 200', () => pm.response.to.have.status(200));"
      ]
     }
    }
   ]
  },
  {
   "name": "Baixar último PDF gerado",
   "request": {
    "method": "GET",
    "url": {
     "raw": "{{baseUrl}}{{pdf_url}}",
     "host": [
      "{{baseUrl}}{{pdf_url}}"
     ]
    },
    "description": "Usa o `pdf_url` salvo pelo request \"Detalhar documento\" (o Render agora é assíncrono e não devolve URL). Em produção será uma URL assinada do S3."
   },
   "event": [
    {
     "listen": "test",
     "script": {
      "type": "text/javascript",
      "exec": [
       "pm.test('status 200', () => pm.response.to.have.status(200));",
       "pm.test('é um PDF', () => pm.expect(pm.response.headers.get('Content-Type')).to.include('pdf'));"
      ]
     }
    }
   ]
  }
 ]
}
