11 KiB
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/apiReturns a JSON blob withServiceName,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, though19(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 70000–79999) 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:
- HTTP Basic Authentication + Access Token/Secret (the standard flow)
- ASP.NET Forms Authentication (cookie-based, browser/JS clients on same domain)
- 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:
{
"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
upnclaim must beusername@domainformat and match a Polaris staff user. - Response shape is identical to the Basic Auth flow (still returns
AccessToken/AccessSecretto 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
Authorizationheader — the key goes in thex-api-keyheader. - 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).
{
"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-Authorizationheader, orPolaris-Override-Authorizationcustom 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:
"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.SystemNotificationsor similar) or asking the vendor if you need the full list. deliveryOptionis documented as only accepting1(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. productIdfor this call is most likely7(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 nositeDomain,organizationId, orworkstationIdsegments at all. Calling the full-length URL returns a404with an empty body (a routing miss, not an app-level error — Polaris's real error responses come back as JSON withMessage/ErrorCode). This exact URL shape isn't visible anywhere in the rendered help page text; it only shows up as atitle="{version}/{language}/{productId}"attribute on the<span>wrapping the "..." inPOST /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.