# Remote Poster Posts notices to a Polaris ILS database via the Polaris Leap (PAPI) API. ## Setup 1. (Recommended) create and activate a virtual environment, then install dependencies: ``` python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` On macOS with Homebrew Python you may get an "externally managed environment" error if you skip the venv and `pip install` directly — the venv avoids that. 2. Copy `.env.example` to `.env` and fill in your server, org/workstation IDs, and staff login. `.env` is gitignored, so real credentials never get committed. - `POLARIS_USERNAME` **must** include the domain, e.g. `MYLIB\Aladdin`. - `POLARIS_ORGANIZATION_ID` should match the `OrganizationID` your staff login actually belongs to (visible in the `PolarisUser` object returned on successful auth) — it's your *logged-on* org context, not the list of orgs you're posting notices to. - `POLARIS_PRODUCT_ID` defaults to `7` (Notices). If calls fail, `19` (PolarisApplicationServices) is the confirmed working alternative — see Notes below. ## Usage ``` python post_notices.py --notice-type 2 --org-ids 3,4,5,6,7,8 ``` - `--notice-type`: the Notification Type ID to post (from your Polaris system tables). - `--org-ids`: comma-separated Organization IDs to post notices *for* (the branches whose notices go out) — separate from `POLARIS_ORGANIZATION_ID` in `.env`, which is your login context. - `--delivery-option`: optional, defaults to `1` (Mail) — currently the only value Polaris accepts. On success it prints a confirmation. On failure (bad credentials, invalid IDs, etc.) it prints the error from Polaris and exits non-zero. Every run sends real notices to real patrons — there's no dry-run mode. Double-check `--notice-type` and `--org-ids` before running. ## Notes on the API (things the published docs get wrong or omit) While wiring this up against Osceola's live server, two things in the reference doc (`docs/polaris-papi-reference.md`) turned out to be incomplete or misleading: 1. **The staff authentication URL is shorter than every other endpoint's.** Most PAPI routes follow `/api/{version}/{language}/{productId}/{siteDomain}/{organizationId}/{workstationId}/...`, but `authentication/staffuser` only uses `/api/{version}/{language}/{productId}/authentication/staffuser` — no `siteDomain`, `organizationId`, or `workstationId`. This makes sense in hindsight (those three describe a *logged-on* session, and you're not logged on yet when authenticating), but it isn't spelled out anywhere in the rendered help page — it's only visible as a `title="..."` tooltip attribute in the raw HTML of `polaris.applicationservices/help/authentication/post_staffuser`, which a normal page read (rendered text, or an AI summary of the page) won't surface. `polaris_client.py` builds this URL separately from every other call (`self.auth_url` vs `self.base_url`). 2. **`productId`**: `19` (PolarisApplicationServices) is confirmed working against the live server; `7` (Notices) was the doc's best guess but untested — worth trying if `19` ever stops working, but don't be surprised if it 400s/404s. If you hit a `404` with an empty response body on any call, that almost always means the URL path itself doesn't match a route (wrong segment count, wrong product ID, etc.) rather than a credentials problem — Polaris's own application-level errors come back as JSON with a `Message`/`ErrorCode`, not an empty body.