{
  "info": {
    "_postman_id": "56ff07e9-f299-4b34-a767-ecbf4515c235",
    "name": "Bubllio CRM API",
    "description": "Authenticated, organization-scoped local API collection. Run the JWT login request first, then use the stored access token.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [{"key": "token", "value": "{{access_token}}", "type": "string"}]
  },
  "item": [
    {
      "name": "Authentication",
      "item": [
        {
          "name": "Login",
          "event": [{"listen": "test", "script": {"type": "text/javascript", "exec": [
            "pm.test(\"Status is 200\", () => pm.response.to.have.status(200));",
            "const data = pm.response.json();",
            "pm.environment.set(\"access_token\", data.access);",
            "pm.environment.set(\"refresh_token\", data.refresh);"
          ]}}],
          "request": {
            "auth": {"type": "noauth"},
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"username\": \"{{username}}\",\n  \"password\": \"{{password}}\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/auth/token/"
          }
        },
        {
          "name": "Refresh access token",
          "event": [{"listen": "test", "script": {"type": "text/javascript", "exec": [
            "pm.test(\"Status is 200\", () => pm.response.to.have.status(200));",
            "const data = pm.response.json();",
            "pm.environment.set(\"access_token\", data.access);",
            "if (data.refresh) pm.environment.set(\"refresh_token\", data.refresh);"
          ]}}],
          "request": {
            "auth": {"type": "noauth"},
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"refresh\": \"{{refresh_token}}\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/auth/token/refresh/"
          }
        },
        {
          "name": "Current user",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/auth/me/"}
        }
      ]
    },
    {
      "name": "Organizations",
      "item": [
        {
          "name": "List Organizations",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/"}
        },
        {
          "name": "Create Organization",
          "event": [{
            "listen": "test",
            "script": {"type": "text/javascript", "exec": [
              "pm.test(\"Status is 201\", () => pm.response.to.have.status(201));",
              "const data = pm.response.json();",
              "if (data.id) pm.environment.set(\"organization_id\", data.id);"
            ]}
          }],
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"name\": \"Nerds Lab\",\n  \"slug\": \"nerds-lab\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/",
            "description": "Installation administrator only. Creates an organization and makes the administrator its owner."
          }
        },
        {
          "name": "Get Organization",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/"}
        },
        {
          "name": "Delete Organization",
          "request": {
            "method": "DELETE",
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/",
            "description": "Owner only."
          }
        }
      ]
    },
    {
      "name": "Installation Administrators",
      "item": [
        {
          "name": "List Installation Administrators and Invitations",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/installation-admin-invitations/"}
        },
        {
          "name": "Invite Installation Administrator",
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"email\": \"it-colleague@example.com\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/installation-admin-invitations/",
            "description": "Installation administrator only. Sends a seven-day invitation through fallback SMTP. Acceptance grants installation privileges without workspace membership."
          }
        }
      ]
    },
    {
      "name": "Members",
      "item": [
        {
          "name": "List Invitation History",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/invitations/", "description": "Returns the latest 100 invitations with pending, accepted, expired, or revoked status."}
        },
        {
          "name": "List Members",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/members/"}
        },
        {
          "name": "Invite Member",
          "event": [{"listen": "test", "script": {"type": "text/javascript", "exec": [
            "const data = pm.response.json();",
            "if (data.id) pm.environment.set(\"invitation_id\", data.id);"
          ]}}],
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"email\": \"teammate@example.com\",\n  \"role\": \"member\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/invitations/",
            "description": "Sends an email invitation; the recipient must accept it before membership exists."
          }
        },
        {
          "name": "Resend Invitation",
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/invitations/{{invitation_id}}/resend/",
            "description": "Sends a fresh seven-day link and revokes earlier active links for the same email."
          }
        },
        {
          "name": "Revoke Invitation",
          "request": {
            "method": "DELETE",
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/invitations/{{invitation_id}}/",
            "description": "Immediately invalidates an unaccepted invitation link."
          }
        },
        {
          "name": "Change Member Role",
          "request": {
            "method": "PATCH",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"role\": \"viewer\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/members/{{membership_id}}/"
          }
        },
        {
          "name": "Remove Member",
          "request": {"method": "DELETE", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/members/{{membership_id}}/"}
        }
      ]
    },
    {
      "name": "Email Accounts",
      "item": [
        {
          "name": "List Email Accounts",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/email-accounts/"}
        },
        {
          "name": "Create SMTP Account",
          "event": [{
            "listen": "test",
            "script": {"type": "text/javascript", "exec": [
              "pm.test(\"Status is 201\", () => pm.response.to.have.status(201));",
              "const data = pm.response.json();",
              "if (data.id) pm.environment.set(\"email_account_id\", data.id);"
            ]}
          }],
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"name\": \"Company SMTP\",\n  \"provider\": \"smtp\",\n  \"host\": \"smtp.example.com\",\n  \"port\": 587,\n  \"username\": \"mailer@example.com\",\n  \"password\": \"provider-password\",\n  \"use_tls\": true,\n  \"use_ssl\": false,\n  \"from_email\": \"mailer@example.com\",\n  \"from_name\": \"Bubllio CRM\",\n  \"is_default\": true\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/email-accounts/",
            "description": "Owner/admin only. The password is encrypted and never returned. Set BUBLLIO_EMAIL_ENCRYPTION_KEY on the server first."
          }
        },
        {
          "name": "Test Email Account",
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"recipient\": \"you@example.com\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/email-accounts/{{email_account_id}}/test/",
            "description": "Attempts one explicit test email and records success/failure metadata."
          }
        }
      ]
    },
    {
      "name": "Companies",
      "item": [
        {
          "name": "List Companies",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/"}
        },
        {
          "name": "Search Companies",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/?search={{company_search}}"}
        },
        {
          "name": "Create Company",
          "event": [{
            "listen": "test",
            "script": {"type": "text/javascript", "exec": [
              "pm.test(\"Status is 201\", () => pm.response.to.have.status(201));",
              "const data = pm.response.json();",
              "if (data.id) pm.environment.set(\"company_id\", data.id);"
            ]}
          }],
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"name\": \"Acme Inc\",\n  \"email\": \"hello@acme.test\",\n  \"phone_number\": \"+306900000000\",\n  \"website\": \"https://acme.test\",\n  \"lifecycle_stage\": \"lead\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/"
          }
        },
        {
          "name": "Get Company",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/{{company_id}}/"}
        },
        {
          "name": "Update Company",
          "request": {
            "method": "PATCH",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"name\": \"Acme Partner\",\n  \"lifecycle_stage\": \"customer\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/{{company_id}}/"
          }
        },
        {
          "name": "Delete Company",
          "request": {"method": "DELETE", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/companies/{{company_id}}/"}
        }
      ]
    },
    {
      "name": "Contacts",
      "item": [
        {
          "name": "List Contacts",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/contacts/"}
        },
        {
          "name": "Search Contacts",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/contacts/?search={{contact_search}}"}
        },
        {
          "name": "Create Contact",
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"company\": \"{{company_id}}\",\n  \"first_name\": \"Maria\",\n  \"last_name\": \"Papadopoulou\",\n  \"email\": \"maria@acme.test\",\n  \"department\": \"Accounting\"\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/contacts/"
          }
        }
      ]
    },
    {
      "name": "Automations",
      "description": "Current Django implementation: one company.created trigger and one fixed send_email action per rule. No worker required. Create a NEW company after saving a rule to exercise dispatch; existing companies are not replayed. Other declared triggers are unwired. SMTP accounts are not yet used by automations.",
      "item": [
        {
          "name": "List Automations",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/automations/"}
        },
        {
          "name": "Create Company Email Automation",
          "event": [{
            "listen": "test",
            "script": {"type": "text/javascript", "exec": [
              "pm.test(\"Status is 201\", () => pm.response.to.have.status(201));",
              "const data = pm.response.json();",
              "if (data.id) pm.environment.set(\"automation_id\", data.id);"
            ]}
          }],
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"name\": \"Notify accounting\",\n  \"trigger\": \"company.created\",\n  \"action_type\": \"send_email\",\n  \"action_config\": {\n    \"to\": [\"accounting@example.com\"],\n    \"subject\": \"New company\",\n    \"body\": \"A new company was added.\"\n  },\n  \"is_active\": true\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/automations/"
          }
        },
        {
          "name": "Test Automation",
          "request": {
            "method": "POST",
            "header": [{"key": "Content-Type", "value": "application/json"}],
            "body": {"mode": "raw", "raw": "{\n  \"payload\": {\n    \"test\": true,\n    \"source\": \"postman\"\n  }\n}"},
            "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/automations/{{automation_id}}/test/",
            "description": "Executes the real action and records a run, even for inactive rules. Does not create a company or verify event dispatch. HTTP 201 can contain status=failed: inspect status and run.error_message. Payload is logged, not interpolated into email. Checked-in transport is console output."
          }
        },
        {
          "name": "List Automation Runs",
          "request": {"method": "GET", "url": "{{base_url}}/api/{{api_version}}/organizations/{{organization_id}}/automations/runs/"}
        }
      ]
    }
  ]
}
