{
  "info": {
    "_postman_id": "e95a7c55-d28b-4c68-a47a-6db91b4bea6e",
    "name": "PyDecode API v1",
    "description": "Authenticated Python bytecode recovery. Set api_key and, for uploads, source_file and filename in the environment. Collection tests validate the API version, response shape, and job chaining.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "pm.test('Request ID is present', function () {",
          "  pm.expect(pm.response.headers.get('X-Request-Id')).to.be.a('string').and.not.empty;",
          "});",
          "pm.test('API version is v1', function () {",
          "  pm.expect(pm.response.headers.get('X-API-Version')).to.eql('v1');",
          "});",
          "pm.test('Rate limit metadata is present', function () {",
          "  pm.expect(pm.response.headers.get('X-RateLimit-Limit')).to.eql('120');",
          "  pm.expect(Number(pm.response.headers.get('X-RateLimit-Remaining'))).to.be.at.least(0);",
          "});"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "1. Test connection",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/",
          "host": ["{{base_url}}"],
          "path": [""]
        },
        "description": "Validate the key and discover the stable v1 endpoints."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Connection succeeds', function () { pm.response.to.have.status(200); });",
              "pm.test('Identity response is valid', function () {",
              "  const body = pm.response.json();",
              "  pm.expect(body.name).to.eql('PyDecode API');",
              "  pm.expect(body.version).to.eql('v1');",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "2. Check credits",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/credits?limit=10",
          "host": ["{{base_url}}"],
          "path": ["credits"],
          "query": [{ "key": "limit", "value": "10" }]
        },
        "description": "Read the shared dashboard/API balance and recent ledger entries."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Credits load', function () { pm.response.to.have.status(200); });",
              "pm.test('Balance is numeric', function () {",
              "  const body = pm.response.json();",
              "  pm.expect(body.credits_available).to.be.a('number');",
              "  pm.expect(body.ledger).to.be.an('array');",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "3. Upload PYC or ZIP",
      "request": {
        "method": "POST",
        "header": [
          { "key": "Idempotency-Key", "value": "{{$guid}}", "type": "text" },
          { "key": "X-Filename", "value": "{{filename}}", "type": "text" },
          {
            "key": "Content-Type",
            "value": "application/octet-stream",
            "type": "text"
          }
        ],
        "body": {
          "mode": "file",
          "file": { "src": "{{source_file}}" }
        },
        "url": {
          "raw": "{{base_url}}/decompile",
          "host": ["{{base_url}}"],
          "path": ["decompile"]
        },
        "description": "Choose a local .pyc or .zip as source_file. This production request reserves credits."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Job is accepted or replayed', function () { pm.expect(pm.response.code).to.be.oneOf([200, 202]); });",
              "if (pm.response.code === 200 || pm.response.code === 202) {",
              "  const body = pm.response.json();",
              "  pm.test('Job response has an ID', function () { pm.expect(body.job.id).to.be.a('string').and.not.empty; });",
              "  pm.collectionVariables.set('job_id', body.job.id);",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "4. List jobs",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/jobs?limit=10",
          "host": ["{{base_url}}"],
          "path": ["jobs"],
          "query": [{ "key": "limit", "value": "10" }]
        }
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Jobs load', function () { pm.response.to.have.status(200); });",
              "pm.test('Page shape is valid', function () {",
              "  const body = pm.response.json();",
              "  pm.expect(body.data).to.be.an('array');",
              "  pm.expect(body).to.have.property('next_cursor');",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "5. Get job",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/jobs/{{job_id}}",
          "host": ["{{base_url}}"],
          "path": ["jobs", "{{job_id}}"]
        }
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Job loads', function () { pm.response.to.have.status(200); });",
              "pm.test('Job state is present', function () { pm.expect(pm.response.json().job.state).to.be.a('string'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "6. Download result",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/jobs/{{job_id}}/result",
          "host": ["{{base_url}}"],
          "path": ["jobs", "{{job_id}}", "result"]
        },
        "description": "Use Send and Download after the job reaches succeeded or partial."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Result is ready or still processing', function () { pm.expect(pm.response.code).to.be.oneOf([200, 409]); });",
              "if (pm.response.code === 200) {",
              "  pm.test('Response is a ZIP', function () { pm.expect(pm.response.headers.get('Content-Type')).to.include('application/zip'); });",
              "}"
            ]
          }
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "base_url",
      "value": "https://pydecode.com/api/v1",
      "type": "string"
    },
    { "key": "api_key", "value": "", "type": "string" },
    { "key": "source_file", "value": "", "type": "string" },
    { "key": "filename", "value": "module.pyc", "type": "string" },
    { "key": "job_id", "value": "", "type": "string" }
  ]
}
