Initial commit

This commit is contained in:
2026-07-09 14:41:31 -04:00
commit df65dc1dee
7 changed files with 582 additions and 0 deletions
+310
View File
@@ -0,0 +1,310 @@
# Polaris.ApplicationServices (PAPI) API Reference
Source: Osceola Library System's Polaris.ApplicationServices help docs
(`https://osceola.librarycatalog.info/polaris.applicationservices/help/...`)
Compiled for internal reference to support scripting against the Notices endpoint.
---
## 1. The API — Basics
- Base API version: `v1` (only supported version)
- Status check URL: `https://[your server name]/Polaris.ApplicationServices/api`
Returns a JSON blob with `ServiceName`, `Version`, `Major`, `Minor`, `Build`, `Revision`.
### URI structure
All URIs (other than API status and staff authentication) include these route segments, in order:
```
/api/{version}/{language}/{productId}/{siteDomain}/{organizationId}/{workstationId}/...
```
| Segment | Type | Required | Notes |
|---|---|---|---|
| `version` | string | Yes | `"v1"` |
| `language` | string | Yes | ISO 639-2 code — `eng`, `spa`, `fre`, `chi`, `ara`, `fas`, `hat`, `haw`, `kor`, `rus`, `vie`, etc. |
| `productId` | integer | Yes | See product ID table below |
| `siteDomain` | string | Yes | Customer site domain (default is `"polaris"`) |
| `organizationId` | integer | Yes | Logged-on organization ID |
| `workstationId` | integer | Yes | Logged-on workstation ID |
Example full URI:
```
/api/v1/eng/19/polaris/1/1/patrons/{id}/basicdata
```
Docs often show this in shorthand as `/api/.../patrons/{id}/basicdata`.
### Product IDs
| Value | Product |
|---|---|
| 0 | All |
| 1 | PowerPAC |
| 2 | ChildrensPAC |
| 3 | ActivePAC |
| 4 | ExpressCheck |
| 5 | InboundTelephony |
| 6 | OutboundTelephony |
| 7 | Notices |
| 8 | ILL |
| 9 | PolarisFusion |
| 10 | AcquisitionsExchange |
| 11 | MobilePAC |
| 12 | SIPService |
| 13 | NCIPService |
| 14 | ERMSPortal |
| 15 | Receipts |
| 16 | ContentXChange |
| 17 | PolarisImport |
| 18 | PolarisAPIConsumerService |
| 19 | PolarisApplicationServices |
| 20 | StaffWebClient |
| 21 | StaffClient |
| 22 | PolarisDatabase |
| 23 | SystemOutput |
| 24 | Vega |
> Since we're calling the Notices endpoint, `productId = 7` (Notices) is likely the semantically "correct" one to use, though `19` (PolarisApplicationServices) may also work depending on how the server is configured — worth confirming against a working example if you have one.
### HTTP response codes
| Code | Status | Notes |
|---|---|---|
| 200 | OK | Success |
| 201 | Created | Returns created object |
| 204 | No Content | Successful deletion |
| 400 | Bad Request | Validation errors, malformed params, etc. Body includes `Message`/`MessageDetail`, sometimes `ErrorCode`. |
| 401 | Unauthorized | `{"ErrorCode":10001,"Message":"User authentication failed."}` |
| 403 | Forbidden | Permission not granted; response includes override info (see Security section) |
| 404 | Not Found | Either "no matching route" or "record not found" (e.g. `ErrorCode 60027`) |
| 405 | Method Not Allowed | Wrong HTTP verb for the resource |
| 409 | Conflict | Object lock (`ErrorCode` 7000079999) or secured record (`ErrorCode` 10004) |
| 411 | Length Required | Missing `Content-Length` |
| 422 | Unprocessable Entity | Body isn't valid JSON |
| 500+ | Server Error | Unhandled exception |
---
## 2. Security and Authentication
Three supported auth mechanisms:
1. **HTTP Basic Authentication + Access Token/Secret** (the standard flow)
2. **ASP.NET Forms Authentication** (cookie-based, browser/JS clients on same domain)
3. **Bearer Token via OAuth2** (OpenID Connect + PKCE)
Plus: **Proxy-Authorization** for privilege overrides.
### 2.1 Basic Auth → Access Token/Secret (primary flow for scripting)
Step 1 — Call the staff auth endpoint with HTTP Basic Auth:
```
POST /api/.../authentication/staffuser
Authorization: Basic <base64(DOMAIN\username:password)>
```
- Username **must include the domain**, e.g. `MYLIB\Aladdin`.
- Combine as `"DOMAIN\username:password"`, then base64-encode the whole string.
- No request body.
Step 2 — Response contains an `AccessToken` and `AccessSecret`:
```json
{
"SiteDomain": "polaris",
"UserDomain": "iii.com",
"AccessToken": "NXmeihFv2kq6meg3EdYoenv2VagJrPHs",
"AccessSecret": "odXCBZuhXBkbwSo4",
"AuthExpDate": "2013-03-26T10:41:11.103",
"PolarisUser": {
"PolarisUserID": 923,
"OrganizationID": 3,
"Name": "Young",
"BranchID": null,
"Enabled": true,
"CreatorID": 895,
"ModifierID": null,
"CreationDate": "2011-02-16T20:28:16.177",
"ModificationDate": null
},
"ERMSNetworkAddress": "young-lt2.polarislibrary.com",
"DataSource": "RD-POLARIS"
}
```
Step 3 — Use the token/secret on all subsequent calls, in a custom `PAS` auth scheme (not base64-encoded):
```
Authorization: PAS {siteDomain}:{AccessToken}:{AccessSecret}
```
Example:
```
Authorization: PAS polaris:NXmeihFv2kq6meg3EdYoenv2VagJrPHs:odXCBZuhXBkbwSo4
```
### 2.2 Bearer Token (OAuth2) variant of staff auth
```
POST /api/.../authentication/staffuser/oauth
Authorization: Bearer <JWT access token>
```
- The JWT's `upn` claim must be `username@domain` format and match a Polaris staff user.
- Response shape is identical to the Basic Auth flow (still returns `AccessToken`/`AccessSecret` to use for follow-on calls).
### 2.3 API Key Authentication (alternative, no token/secret round-trip)
```
POST /api/.../authentication/staffuser
x-api-key: <your API key>
```
- No `Authorization` header — the key goes in the `x-api-key` header.
- Response does **not** include `AccessToken`/`AccessSecret` (they're blank strings) — the API key itself is presumably reused directly on subsequent calls (docs don't show that call shape explicitly, so confirm against a working example).
```json
{
"SiteDomain": "",
"UserDomain": "",
"AccessToken": "",
"AccessSecret": "",
"AuthExpDate": null,
"PolarisUser": {
"PolarisUserID": 1,
"OrganizationID": 5,
"Name": "PolarisExec",
"BranchID": null,
"Enabled": true,
"CreatorID": 1,
"ModifierID": 1,
"CreationDate": null,
"ModificationDate": "2020-12-11T10:28:20.69-05:00"
},
"ERMSNetworkAddress": "clvitn-pc197yjl.polarislibrary.com",
"DataSource": "CLVITN-PC197YJL",
"SiteCode": "MIS/"
}
```
### 2.4 Privilege overrides (403 handling)
If a call returns `403 Forbidden` because the authenticated user lacks a permission, you can supply override credentials via:
- `Proxy-Authorization` header, **or**
- `Polaris-Override-Authorization` custom header (supports multiple overrides per call)
Format (before base64 encoding):
```
username:password:permissionid:permissionownerid
```
Example:
```
Proxy-Authorization: Basic cG9sYXJpc2V4ZWNAZG9tYWluOnBhc3N3b3JkOjgzOjEwMw==
```
Multiple overrides via repeated headers or a comma-delimited list of `Basic ...` values in one header.
**Note:** the primary `Authorization` header (token/secret) is still required alongside any override header.
---
## 3. HTTP Headers Cheat Sheet
**GET requests** — set `Accept`:
```
Accept: application/json
```
(also valid: `text/json`, `image/jpg` for binary/image responses)
**PATCH / POST / PUT requests** — set `Content-Type`:
```
Content-Type: application/json
```
**Auth headers**:
```
Authorization: PAS {siteDomain}:{AccessToken}:{AccessSecret}
Proxy-Authorization: Basic <base64 override>
```
---
## 4. Notices — POST (the endpoint we care about)
### Post notices to the database
```
POST /api/.../notices/post?noticeType={notificationtypeid}&deliveryOption={deliveryoptionid}
```
#### Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| `noticeType` | integer | Yes | ID of the Notification Type |
| `deliveryOption` | integer | Yes | ID of the Delivery Option. **Only valid value is Mail (`1`).** |
#### Request body
A JSON string containing a comma-separated list of Organization IDs:
```json
"3,5,7"
```
#### Response
Returns a plain boolean.
- Success: `true`
- Failure: `false`
#### HTTP response codes
| Code | Meaning |
|---|---|
| 200 | OK. Success |
| 400 | Failure — bad request, invalid `noticeType`, or invalid `deliveryOption` |
#### Notes / open questions for implementation
- The docs don't enumerate valid `noticeType` (Notification Type) IDs on this page — that's presumably in Polaris's system tables or the "System Administration" section elsewhere in the docs (e.g. Parameters and Profiles, or a lookup table). Worth checking the Polaris database (`Polaris.SystemNotifications` or similar) or asking the vendor if you need the full list.
- `deliveryOption` is documented as only accepting `1` (Mail) right now, despite the API allowing an integer — so don't expect e.g. email/SMS options to work here even if other DeliveryOptions endpoints list more.
- The response being a bare `true`/`false` (not an object) means your client code should parse the raw JSON boolean rather than expecting a structured object — easy to trip up if you assume every endpoint returns a DTO.
- `productId` for this call is most likely `7` (Notices) per the product ID table — worth confirming with a test call.
---
## 5. Corrections confirmed against a live server (Osceola)
While building `post_notices.py` against Osceola's live Polaris.ApplicationServices instance,
two things in this doc turned out to be wrong or incomplete:
- **Staff authentication URL is shorter than section 1 implies.** The "all URIs other than API
status and staff authentication" caveat in section 1 undersells how different the auth route
is. It is **not** `/api/{version}/{language}/{productId}/{siteDomain}/{organizationId}/{workstationId}/authentication/staffuser`
— it's just `/api/{version}/{language}/{productId}/authentication/staffuser`, with no
`siteDomain`, `organizationId`, or `workstationId` segments at all. Calling the full-length
URL returns a `404` with an **empty body** (a routing miss, not an app-level error — Polaris's
real error responses come back as JSON with `Message`/`ErrorCode`). This exact URL shape isn't
visible anywhere in the rendered help page text; it only shows up as a `title="{version}/{language}/{productId}"`
attribute on the `<span>` wrapping the "..." in `POST /api/.../authentication/staffuser`, i.e.
you have to view-source the page (`polaris.applicationservices/help/authentication/post_staffuser`)
to find it — a plain fetch or rendered read of the page won't surface it.
- **`productId = 19`** (PolarisApplicationServices) is confirmed working end-to-end (auth +
`notices/post`) against Osceola's server. `productId = 7` (Notices) — this doc's original best
guess — was not confirmed; it may still be correct, but wasn't tested.
---
## 6. Gaps in this doc (pages that returned no additional content)
Two pages in the doc set — **Common Response Structures** and **Collections** (both under "Introduction") — returned only their page heading with no body content when fetched. This might mean:
- The content is loaded client-side via JavaScript that a simple page fetch won't execute, or
- Those particular pages are thin/placeholder pages in this Polaris version.
If you need the actual content of those two pages (e.g. for handling paginated list responses in the `notices` or other endpoints), it may be worth viewing them directly in a browser to check whether there's dynamically-loaded content, since a plain HTTP fetch didn't surface anything under those headings.