{
  "info": {
    "_postman_id": "d8b24d0c-db07-4cec-9128-cef1bf80333d",
    "name": "EMTITHAL API",
    "description": "\n\nVersion: 0.3.1\nBase URL: {{base_url}}\nAuth: Bearer JWT — call POST /api/v1/auth/login with email + password; copy the access_token into the `jwt_token` environment variable.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "admin",
      "item": [
        {
          "name": "Kick off the ingestion workflow for a code-edition",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/admin/code-editions/:edition_id/ingest",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "code-editions",
                ":edition_id",
                "ingest"
              ],
              "variable": [
                {
                  "key": "edition_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Starts `code_ingest.IngestCodeWorkflow` on Temporal with the tenant_id + code_edition_id threaded into the workflow input."
          },
          "response": []
        },
        {
          "name": "List rules pending engineer review for a code-edition",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/admin/code-editions/:edition_id/review-queue",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "code-editions",
                ":edition_id",
                "review-queue"
              ],
              "variable": [
                {
                  "key": "edition_id",
                  "value": "",
                  "description": "path parameter"
                }
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "offset",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "Paginated list of rules awaiting engineer approval. RLS scopes the read to the caller's tenant; the handler only paginates."
          },
          "response": []
        },
        {
          "name": "Register a new tenant-scoped code-edition",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/admin/code-editions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "code-editions"
              ]
            },
            "description": "Create a new row in `compliance.code_editions` with `tenant_id = caller's tenant`. Returns the new edition id + presigned PUT URLs for the source PDFs the caller will upload.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code_id\": \"Short identifier for the code (e.g. '801', '201', 'ACME-FIRE').\",\n  \"code_long_name\": \"Human-readable name shown in workbench UI + LLM prompts.\",\n  \"edition_year\": 0,\n  \"jurisdiction\": \"ISO-like jurisdiction tag (e.g. 'SA', 'AE').\",\n  \"language\": \"BCP-47 tag or composite (e.g. 'en', 'ar', 'ar+en').\",\n  \"source_pdfs_count\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Return ingestion status + rule counts for a code-edition",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/admin/code-editions/:edition_id/status",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "code-editions",
                ":edition_id",
                "status"
              ],
              "variable": [
                {
                  "key": "edition_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Return the ingestion lifecycle status + rule counts."
          },
          "response": []
        }
      ]
    },
    {
      "name": "auth",
      "item": [
        {
          "name": "Authenticate and obtain session tokens",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/login",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "login"
              ]
            },
            "description": "Log in with email and password. On success, returns access token (JWT, typically 1h TTL) and sets httpOnly refresh cookie (14 days, SameSite=Lax). Use access token in Authorization: Bearer header for subsequent requests. Refresh token binds to the issued access token (H-24 binding prevents token mixups).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"engineer@example.sa\",\n  \"password\": \"Account password.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Log out and invalidate session",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/logout",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "logout"
              ]
            },
            "description": "Invalidate the current session. The refresh token is revoked in Redis and the cookie is deleted. Subsequent API calls without a fresh access token will fail with 401. The access token itself remains valid until exp, but is typically discarded client-side."
          },
          "response": []
        },
        {
          "name": "Register a new user account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/register",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "register"
              ]
            },
            "description": "Register a new user with email and password. A verification OTP is sent to the email; call POST /verify-email with the OTP to activate. Password must be 12+ characters, with complexity enforced (3 of 4 character classes or 16+ character passphrase).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"engineer@example.sa\",\n  \"eula_sha256\": \"SHA-256 (64 hex chars) of the exact EULA document the user accepted.\",\n  \"eula_version\": \"Version identifier of the EULA the user accepted.\",\n  \"password\": \"Password (12-128 chars). Must use 3 of 4 character classes (lowercase, uppercase, digit, special) or be 16+ characters long.\",\n  \"preferred_lang\": \"en\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Request a password-reset code",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/request-password-reset",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "request-password-reset"
              ]
            },
            "description": "Send a 6-digit password-reset code to the given email. The response is DELIBERATELY identical whether or not the email is registered — this endpoint cannot be used to discover which addresses have accounts. Call POST /reset-password with the code to complete.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"engineer@example.sa\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Reset password using the emailed code",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/reset-password",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "reset-password"
              ]
            },
            "description": "Set a new password using the 6-digit code from POST /request-password-reset. On success the password is changed and ALL existing sessions are revoked (the user must log in again). Errors are generic (no account-existence disclosure).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"engineer@example.sa\",\n  \"new_password\": \"New password (12-128 chars). Must use 3 of 4 character classes (lowercase, uppercase, digit, special) or be 16+ characters.\",\n  \"otp\": \"6-digit reset code sent to the email.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Retrieve current user profile",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/me",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "me"
              ]
            },
            "description": "Return the authenticated user's profile (email, tier, language preference, status). Requires valid Authorization: Bearer access_token header."
          },
          "response": []
        },
        {
          "name": "Rotate access token using refresh cookie",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/refresh",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "refresh"
              ]
            },
            "description": "Exchange the refresh cookie + current access token for a new access token pair. The refresh token is rotated on each call (H-24: binding to access_token.jti prevents reuse). Requires both the refresh cookie (httpOnly, sent automatically by the browser) and Authorization: Bearer header with the current access token (even if expired by exp claim). On refresh failure, the refresh cookie is cleared (user bounced to login)."
          },
          "response": []
        },
        {
          "name": "Verify email and activate account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/auth/verify-email",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "verify-email"
              ]
            },
            "description": "Complete email verification by submitting the 6-digit OTP sent to the registered email. On success, returns access token and sets httpOnly refresh cookie. The refresh token is bound to the access token (H-24 binding).",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"otp\": \"6-digit OTP sent to the registered email address.\",\n  \"user_id\": \"00000000-0000-0000-0000-000000000000\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "billing",
      "item": [
        {
          "name": "Create a subscription for the caller (sign-up)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/subscription",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "subscription"
              ]
            },
            "description": "Creates a new billing subscription for the authenticated user.  Accepts plan_code, billing_mode, billing_cadence, and optional PAYG settings.  Returns 409 if a subscription already exists; call DELETE + reactivate to switch plans.  Returns 404 if the specified plan_code is not found in the catalog.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_cadence\": null,\n  \"billing_mode\": null,\n  \"payer_organisation_id\": \"00000000-0000-0000-0000-000000000000\",\n  \"payg_monthly_ceiling_sar\": 0.0,\n  \"payg_opted_in\": false,\n  \"plan_code\": null\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get a single plan by code",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/plans/:plan_code",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "plans",
                ":plan_code"
              ],
              "variable": [
                {
                  "key": "plan_code",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Returns the full billing.plans row for a single plan identified by its code (e.g. 'free', 'pro', 'enterprise').  Useful for building plan-detail or upgrade-confirmation screens.  Returns 404 if the plan code is unknown."
          },
          "response": []
        },
        {
          "name": "Get the caller's current-period usage meter",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/usage",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "usage"
              ]
            },
            "description": "Returns a snapshot of the caller's billing.usage_meters row covering the present moment.  Useful for in-app dashboards showing 'X reviews remaining', 'Y GB used', running PAYG charges, etc.  Returns 404 if the caller has no active subscription (and therefore no current meter)."
          },
          "response": []
        },
        {
          "name": "Get the caller's subscription (RLS-scoped)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/subscription",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "subscription"
              ]
            },
            "description": "Returns the active subscription row for the authenticated user; scoped by RLS to the caller's tenant.  Returns 404 if the caller has no subscription yet (e.g. new sign-up before selecting a plan)."
          },
          "response": []
        },
        {
          "name": "List published plan catalog",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/plans",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "plans"
              ]
            },
            "description": "Returns the public plan catalog ordered Free -> Pro -> Enterprise.  All authenticated users see the same list (catalog is not RLS-scoped).  Useful for billing UI: render the pricing page from this response."
          },
          "response": []
        },
        {
          "name": "Soft-cancel the caller's subscription (effective at period end)",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/subscription",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "subscription"
              ]
            },
            "description": "Sets cancel_at_period_end=true.  The subscription remains 'active' until current_period_end, at which point a renewal worker transitions it to 'expired'.  Returns the updated subscription state.\n\nEnterprise commitments: cancellation BEFORE commitment_end_at is forbidden via this endpoint -- contact support for negotiated early termination."
          },
          "response": []
        },
        {
          "name": "Toggle PAYG opt-in + monthly ceiling",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/subscription/payg",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "subscription",
                "payg"
              ]
            },
            "description": "Enables or disables pay-as-you-go metered billing for the caller's subscription and optionally sets a monthly SAR ceiling.  When opting out, the ceiling is cleared server-side.  Returns 422 if the plan does not support PAYG; returns 404 if no subscription exists.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payg_monthly_ceiling_sar\": 0.0,\n  \"payg_opted_in\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Undo a soft-cancel before the period ends",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/me/subscription/reactivate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "me",
                "subscription",
                "reactivate"
              ]
            },
            "description": "Reverses a prior soft-cancel by clearing cancel_at_period_end.  Only valid while the subscription is still 'active' and before current_period_end.  Returns 404 if no subscription exists, 409 if the subscription is already expired or the state transition is illegal."
          },
          "response": []
        }
      ]
    },
    {
      "name": "codes",
      "item": [
        {
          "name": "Ask a question about a code's rules (bilingual, with citations)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/codes/:code_id/qa",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "codes",
                ":code_id",
                "qa"
              ],
              "variable": [
                {
                  "key": "code_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Returns the top-K most-similar rules + an LLM-synthesized answer. Tenant-scoped via RLS on `compliance.code_editions` + `compliance.rules`. Every successful interaction emits a `CODE_QA_INTERACTION` audit event.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"lang\": \"ar\",\n  \"question_text\": \"string\",\n  \"top_k\": 10\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List code-editions visible to the caller",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/codes",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "codes"
              ]
            },
            "description": "Returns the multi-code platform inventory. Platform codes (tenant_id IS NULL) are visible to every caller; tenant BYO codes (Phase 4) are visible only to the owning tenant via RLS."
          },
          "response": []
        }
      ]
    },
    {
      "name": "health",
      "item": [
        {
          "name": "Liveness probe",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              }
            ],
            "url": {
              "raw": "{{base_url}}/healthz",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "healthz"
              ]
            },
            "description": "Return 200 if the process event loop is responsive. This probe does not check external dependencies; it always succeeds unless the process is frozen or crashing."
          },
          "response": []
        },
        {
          "name": "Readiness probe",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              }
            ],
            "url": {
              "raw": "{{base_url}}/readyz",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "readyz"
              ]
            },
            "description": "Return 200 iff both Aurora PostgreSQL and Redis are reachable. Kubernetes removes the pod from the service load balancer when this returns non-200. Checks are optimized for speed (<2s) using connection-pool pings, not real queries."
          },
          "response": []
        }
      ]
    },
    {
      "name": "jobs",
      "item": [
        {
          "name": "Cancel a running compliance job",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/cancel",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "cancel"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Sends a cancellation signal to the Temporal ComplianceJobWorkflow. Licensed_engineer-only."
          },
          "response": []
        },
        {
          "name": "Create a compliance check job",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs"
              ]
            },
            "description": "Creates a new compliance job for the given project + document set, starts the ComplianceJobWorkflow on Temporal, and returns the job_id immediately. The job runs asynchronously; poll GET /jobs/{job_id} or stream GET /jobs/{job_id}/events for progress.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"codes\": [\n    \"string\"\n  ],\n  \"confirm_classification\": true,\n  \"document_ids\": [\n    \"00000000-0000-0000-0000-000000000000\"\n  ],\n  \"project_id\": \"00000000-0000-0000-0000-000000000000\",\n  \"query\": \"Free-text description of the project scope in Arabic or English. Used by ScopeClassificationActivity to determine occupancy class and building type. Example: 'مشروع مكتب 5 طوابق في الرياض، استخدام تجاري مختلط'.\",\n  \"query_scope\": {},\n  \"region\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get compliance job state",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Returns the current state of a compliance job. When status='completed', the response includes all verdicts and drawing elements."
          },
          "response": []
        },
        {
          "name": "Get verdict PDF download URL",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/result.pdf",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "result.pdf"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Returns an S3 presigned URL for the compliance verdict PDF. Valid for 15 minutes."
          },
          "response": []
        },
        {
          "name": "List compliance jobs for a project",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/projects/:project_id/jobs",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "projects",
                ":project_id",
                "jobs"
              ],
              "variable": [
                {
                  "key": "project_id",
                  "value": "",
                  "description": "path parameter"
                }
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "offset",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "Returns recent compliance jobs for the given project, ordered by created_at descending. Paginated via `limit` and `offset`."
          },
          "response": []
        },
        {
          "name": "List job verdicts in Phase-7 workbench shape (paginated)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/verdicts",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "verdicts"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ],
              "query": [
                {
                  "key": "code_id",
                  "value": "",
                  "description": "Filter by design-code identifier (e.g. '801', '201').",
                  "disabled": true
                },
                {
                  "key": "element_type",
                  "value": "",
                  "description": "Filter by drawing element type (e.g. 'door', 'corridor').",
                  "disabled": true
                },
                {
                  "key": "verdict_state",
                  "value": "",
                  "description": "Filter by verdict state. Accepts the same values as VerdictValue (PASS / FAIL / NEEDS_REVIEW / OVERRIDDEN / NOT_APPLICABLE).",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "",
                  "description": "Maximum number of verdicts to return per page (1-500). Default: 200.",
                  "disabled": true
                },
                {
                  "key": "offset",
                  "value": "",
                  "description": "Zero-based offset for pagination.",
                  "disabled": true
                }
              ]
            },
            "description": "Returns the compliance verdicts for a job as a paginated flat list. AUD-048: verdicts are worst-of-rolled-up per rule_id — a FAIL on any element surfaces as FAIL at rule grain, never masked by a PASS row. AUD-053: results are paginated (limit/offset); total_count is returned so the frontend can render a page indicator. Each verdict carries the multi-code platform fields (code_id, code_edition_id, element_id, citations) per docs/api/verdict-shape.md."
          },
          "response": []
        },
        {
          "name": "Override a compliance verdict",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/override",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "override"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Licensed engineer overrides a compliance verdict. Persisted to projects.job_overrides AND signals the Temporal workflow.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"justification\": \"Required free-text justification for the override. Arabic is preferred (regulatory language). Minimum 20 characters. This text is preserved in projects.job_overrides for 7-year audit retention.\",\n  \"new_verdict\": null,\n  \"original_verdict\": null,\n  \"rule_id\": \"Rule identifier whose verdict is being overridden. Must match a rule_id in compliance.rules. Accepts live corpus IDs such as 'SBC-801-303.7-b77eaf76' (slash-separated taxonomy paths) and future edition-qualified IDs of the form '{code}:{edition}:{section}'.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Per-element verdict drill-down for a single rule (AUD-048)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/rules/:rule_id/verdicts",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "rules",
                ":rule_id",
                "verdicts"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                },
                {
                  "key": "rule_id",
                  "value": "",
                  "description": "path parameter"
                }
              ],
              "query": [
                {
                  "key": "verdict",
                  "value": "",
                  "description": "Filter by verdict value (PASS / FAIL / NEEDS_REVIEW / OVERRIDDEN / NOT_APPLICABLE).",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "",
                  "description": "Page size (1-500).",
                  "disabled": true
                },
                {
                  "key": "offset",
                  "value": "",
                  "description": "Zero-based offset.",
                  "disabled": true
                }
              ]
            },
            "description": "AUD-048: returns ALL element-grained verdict rows for a single rule_id at the current (MAX) version — one row per cited_drawing_element_id. No rollup is applied here; both the FAIL row for door-A and the PASS row for door-B are returned so the workbench can deep-link each element to its position on the canvas. Paginated."
          },
          "response": []
        },
        {
          "name": "Stream job progress events (SSE)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/events",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "events"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Server-Sent Events stream of compliance job progress. Reconnect with Last-Event-ID to resume from the last received sequence."
          },
          "response": []
        }
      ]
    },
    {
      "name": "projects",
      "item": [
        {
          "name": "Create a project",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/projects",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "projects"
              ]
            },
            "description": "Creates a new project (container for uploaded drawings) owned by the authenticated user.  The project name must be 1-200 characters.  Returns the new project row with its UUID; pass this id to POST /api/v1/projects/{project_id}/documents to begin uploading drawings.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Finalize an upload (head + hash the object, register the document)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/projects/:project_id/documents/:document_id/complete",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "projects",
                ":project_id",
                "documents",
                ":document_id",
                "complete"
              ],
              "variable": [
                {
                  "key": "project_id",
                  "value": "",
                  "description": "path parameter"
                },
                {
                  "key": "document_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Verifies that the client's PUT to upload_url succeeded, computes the SHA-256 hash of the uploaded object, enforces the per-user storage quota, and inserts the project_documents row.  Idempotent: retrying an already-completed document_id returns the existing row.  Returns 409 if the object has not been uploaded yet; 413 if it exceeds the 200 MB limit; 402 if the storage quota or PAYG ceiling is exhausted.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"original_filename\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List your projects",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/projects",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "projects"
              ]
            },
            "description": "Returns all projects owned by the authenticated user, scoped by RLS to the caller's tenant.  The list is unordered; clients should sort by created_at if display order matters."
          },
          "response": []
        },
        {
          "name": "Register a document + get a presigned upload URL",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/projects/:project_id/documents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "projects",
                ":project_id",
                "documents"
              ],
              "variable": [
                {
                  "key": "project_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Mints a presigned S3 PUT URL so the client can upload a drawing file directly to S3 without transiting the API.  The returned document_id and upload_url are valid for 15 minutes.  After the PUT completes, call POST .../documents/{document_id}/complete to register the document row.  Returns 404 if the project is not found or the caller does not own it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"content_type\": \"string\",\n  \"original_filename\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "rule-review",
      "item": [
        {
          "name": "Apply incremental corrections to pending review item",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/queue/:queue_id",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "queue",
                ":queue_id"
              ],
              "variable": [
                {
                  "key": "queue_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Update specific fields of a pending review item. Each edited field must have a correction_reason explaining the change. The item remains in 'pending_review' status. Requires licensed_engineer role. Subject to RLS.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"correction_reasons\": {},\n  \"field_edits\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Approve rule and finalize review",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/queue/:queue_id/approve",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "queue",
                ":queue_id",
                "approve"
              ],
              "variable": [
                {
                  "key": "queue_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Finalize review by approving the rule. Any pending field_edits are applied as final. A RULE_REVIEW_APPROVED audit event is emitted with the approval timestamp and reviewer ID. Item transitions to 'approved' status. The rule is now live for compliance checks. Requires licensed_engineer role. Subject to RLS.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"correction_reasons\": {},\n  \"field_edits\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Download SBC rule PDF",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/pdf/:filename",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "pdf",
                ":filename"
              ],
              "variable": [
                {
                  "key": "filename",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Retrieve an SBC rule PDF in either Arabic or English. Only files in the approved manifest set are served (SBC-301-AR.pdf, SBC-301-EN.pdf, SBC-201-AR.pdf, SBC-201-EN.pdf). Path traversal defenses are in place (F-18: canonical path check). Requires authentication."
          },
          "response": []
        },
        {
          "name": "Get detailed review queue item",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/queue/:queue_id",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "queue",
                ":queue_id"
              ],
              "variable": [
                {
                  "key": "queue_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Retrieve full details of a queue item including rule data, extracted values, and pending edits. Subject to RLS (user must be in the queue item's review group). Returns 404 if item not found or user lacks access."
          },
          "response": []
        },
        {
          "name": "List rule review queue items",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/queue",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "queue"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter by queue item status",
                  "disabled": true
                },
                {
                  "key": "rule_type",
                  "value": "",
                  "description": "Filter by SBC rule type (e.g., SBC-301, SBC-201)",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "",
                  "description": "Page size (1-500)",
                  "disabled": true
                },
                {
                  "key": "offset",
                  "value": "",
                  "description": "Pagination offset",
                  "disabled": true
                }
              ]
            },
            "description": "Retrieve paginated review queue items visible to the authenticated user. Filters: status (pending, approved, rejected), rule_type. Results use row-level security (RLS) so users only see items in their review groups."
          },
          "response": []
        },
        {
          "name": "Reject rule and return to submission queue",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/rule-review/queue/:queue_id/reject",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "rule-review",
                "queue",
                ":queue_id",
                "reject"
              ],
              "variable": [
                {
                  "key": "queue_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Reject a pending review item with a reason. A RULE_REVIEW_REJECTED audit event is emitted. The rule is returned to the submission queue for reprocessing (re-extraction or re-entry). The reason is stored for the compliance team. Requires licensed_engineer role. Subject to RLS.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "users",
      "item": [
        {
          "name": "Submit a PDPL data-subject access/erasure request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/users/me/dsar",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "users",
                "me",
                "dsar"
              ]
            },
            "description": "Authenticated data subject (user) submits a DSAR.  Request lands in the manual review queue and is resolved within 30 days per PDPL Article 22.  Returns 202 with the request_id and the resolution deadline timestamp.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"request_details\": \"string\",\n  \"request_type\": \"PDPL Article 17-22 request category.  'access' (Article 17), 'erasure' (Article 19, subject to legal-obligation exemptions for compliance audit records), 'portability' (Article 21), 'rectification' (Article 18), 'objection' (Article 20), 'restriction' (restrict processing pending dispute resolution).\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "versions",
      "item": [
        {
          "name": "Create a new working version of a job (Phase 5 scaffold)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/versions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "versions"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Creates a new immutable version row in `projects.job_versions`. Multi-version-per-job is a Phase 5 deliverable — the table is currently 1:1 with `projects.jobs` so this endpoint is wired as a 501 scaffold. The contract shape (returns `version_id`) is stable so the CTO frontend can be implemented in parallel."
          },
          "response": []
        },
        {
          "name": "Diff verdicts between two versions of a job (Phase 9)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:job_id/versions/:version_a/diff/:version_b",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":job_id",
                "versions",
                ":version_a",
                "diff",
                ":version_b"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "",
                  "description": "path parameter"
                },
                {
                  "key": "version_a",
                  "value": "",
                  "description": "path parameter"
                },
                {
                  "key": "version_b",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Returns `{resolved, new_violations, unchanged}` — the set diff of (element_id, code_id, rule_id, verdict_state) tuples between two versions' verdicts. Used by the workbench to render red/green diff highlights after an engineer's edit triggers re-evaluation. \n\nPhase-5 dependency: multi-version-per-job schema not yet shipped. Until that lands, the only valid request is the self-diff (version_a == version_b == job_id), which returns {resolved: [], new_violations: [], unchanged: <all current verdicts>}. Cross-version requests get a 501."
          },
          "response": []
        }
      ]
    },
    {
      "name": "webhooks",
      "item": [
        {
          "name": "List webhook subscriptions visible to caller (RLS-scoped)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/webhooks/subscriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "webhooks",
                "subscriptions"
              ]
            },
            "description": "Returns all webhook subscriptions belonging to organisations the caller is a member of, ordered by created_at descending.  RLS at the DB layer enforces organisation-scoped visibility so cross-tenant leakage is impossible."
          },
          "response": []
        },
        {
          "name": "Register a webhook subscription",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/webhooks/subscriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "webhooks",
                "subscriptions"
              ]
            },
            "description": "Creates a new webhook subscription that delivers HMAC-signed event payloads to the specified HTTPS endpoint_url.  The signing_secret is stored hashed and never returned; keep the plaintext to verify delivery signatures.  Returns 402 if no active subscription exists or the plan limit is reached; 422 if the endpoint_url is not a publicly-routable HTTPS address; 403 if the caller is not a member of the specified organisation.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"display_name\": \"string\",\n  \"endpoint_url\": \"Customer-controlled HTTPS endpoint receiving webhook deliveries.  MUST be HTTPS (CHECK constraint in migration 030 enforces this).\",\n  \"event_types\": [\n    \"string\"\n  ],\n  \"organisation_id\": \"00000000-0000-0000-0000-000000000000\",\n  \"signing_secret\": \"Customer-generated signing secret used to HMAC-SHA256 the delivered payload.  Min 32 chars (sufficient entropy for HMAC).  Stored HASHED in the DB; the customer keeps the plaintext to verify deliveries.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Send a synchronous test ping to verify endpoint setup",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/webhooks/subscriptions/:subscription_id/test",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "webhooks",
                "subscriptions",
                ":subscription_id",
                "test"
              ],
              "variable": [
                {
                  "key": "subscription_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Send a `webhook.test` event payload to the customer's endpoint with\nthe HMAC signature.  Returns the customer's HTTP status + latency + any\ntransport error.  Always returns HTTP 200 with the result in the body\n(the customer's failure shouldn't surface as our HTTP failure)."
          },
          "response": []
        },
        {
          "name": "Soft-delete (deactivate) a webhook subscription",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/webhooks/subscriptions/:subscription_id",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "webhooks",
                "subscriptions",
                ":subscription_id"
              ],
              "variable": [
                {
                  "key": "subscription_id",
                  "value": "",
                  "description": "path parameter"
                }
              ]
            },
            "description": "Sets is_active=false.  Hard-deletion is admin-only via direct SQL\n(preserves audit history of past subscriptions).  Returns 204 even if\nthe row is already inactive (idempotent)."
          },
          "response": []
        }
      ]
    },
    {
      "name": "webhooks-moyasar",
      "item": [
        {
          "name": "Moyasar payment + subscription webhook intake (body-token verified)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept-Language",
                "value": "ar,en;q=0.9"
              },
              {
                "key": "Authorization",
                "value": "Bearer {{jwt_token}}"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/billing/webhooks/moyasar",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "billing",
                "webhooks",
                "moyasar"
              ]
            },
            "description": "Public endpoint receiving Moyasar payment.* and subscription.* webhook deliveries.  Authentication via merchant-assigned secret_token field in the JSON body (AUD-120).  Returns 202 on success or unknown event; 401 on token mismatch; 400 on malformed payload."
          },
          "response": []
        }
      ]
    }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{jwt_token}}",
        "type": "string"
      }
    ]
  }
}