Node/Express sample app for integrating with the Citewell Public API as an external customer service.
The app lets a developer connect a Citewell enterprise API key, request quotes, submit reports, watch report progress, receive webhooks, and inspect the exact API responses used at each step.
- Checks whether a Citewell API key can reach the public API.
- Shows account, scope, limit, credit, and usage information.
- Requests a quote for a manuscript upload.
- Polls quote status until the quote becomes usable.
- Recovers a lost quote response by
Idempotency-Key. - Submits a report from a quote or directly from an uploaded
.pdfor.docx. - Lists report jobs and polls a single report by ID.
- Recovers a report by
Idempotency-Key. - Cancels queued or running report jobs.
- Renders a succeeded report from the public API JSON response.
- Proxies PDF downloads when a succeeded report includes
pdf.download_url. - Loads the public webhook event catalog.
- Receives webhook deliveries at
POST /webhooks/citewell. - Verifies webhook signatures when the active profile has a webhook secret configured.
- Keeps local polling and webhook logs in memory for comparison.
- Runs a local consistency audit for one report.
| Citewell endpoint | Sample route | Purpose |
|---|---|---|
GET /api/public/v1/ping/ |
GET /api/local/ping |
Credential smoke test |
GET /api/public/v1/account/ |
GET /api/local/account |
Enterprise, key, scopes, limits, and links |
GET /api/public/v1/credits/balance/ |
GET /api/local/credits |
Credit balance and report capacity |
GET /api/public/v1/usage/ |
GET /api/local/usage?range=30d |
Daily API usage rollup |
| Combined account checks | GET /api/local/operating-status |
Local readiness summary |
POST /api/public/v1/reports/quote/ |
POST /api/local/reports/quote |
Start an async quote for a manuscript upload |
GET /api/public/v1/reports/quote/{quote_id}/ |
GET /api/local/reports/quote/:id |
Poll async quote status |
GET /api/public/v1/reports/quote/by-idempotency-key/{key}/ |
GET /api/local/reports/quote/by-idempotency-key/:key |
Recover a lost quote response |
POST /api/public/v1/reports/ |
POST /api/local/reports/confirm-quote |
Submit using quote_id |
POST /api/public/v1/reports/ |
POST /api/local/reports/direct |
Direct manuscript submit |
GET /api/public/v1/reports/ |
GET /api/local/reports |
List report jobs |
GET /api/public/v1/reports/{id}/ |
GET /api/local/reports/:id |
Poll status and result |
GET /api/public/v1/reports/by-idempotency-key/{key}/ |
GET /api/local/reports/by-idempotency-key/:key |
Recover a lost submit response |
DELETE /api/public/v1/reports/{id}/ |
DELETE /api/local/reports/:id |
Cancel a report job |
Report pdf.download_url |
GET /api/local/reports/:id/pdf |
Download the generated report PDF |
GET /api/public/v1/webhooks/events/ |
GET /api/local/webhooks/events |
Webhook event catalog |
| Local-only memory | GET /api/local/logs?report_id={id} |
Polling and webhook logs for one report |
| Local-only memory | GET /api/local/audit/:id |
Consistency audit for one report |
Webhook endpoint registration is not handled by this sample. Configure delivery endpoints in the Citewell dashboard and point them to the app's POST /webhooks/citewell route.
- Node.js 20 or newer.
- A Citewell Public API base URL.
- A Citewell enterprise public API key for sandbox or live.
- Optional: a Citewell webhook endpoint signing secret.
cp .env.example .env
npm installEdit .env. Set CITEWELL_API_BASE_URL to the Citewell API origin, then add one or both API profiles:
PORT=4310
CITEWELL_API_BASE_URL=https://api.citewell.org
CITEWELL_DEFAULT_ENVIRONMENT=test
CITEWELL_TEST_API_KEY=cwpk_test_your_key
CITEWELL_TEST_WEBHOOK_SECRET=whsec_your_test_endpoint_secret
CITEWELL_LIVE_API_KEY=cwpk_live_your_key
CITEWELL_LIVE_WEBHOOK_SECRET=whsec_your_live_endpoint_secret
CITEWELL_API_KEY=
CITEWELL_WEBHOOK_SECRET=CITEWELL_DEFAULT_ENVIRONMENT=test is the conservative default for local testing. If you only have a live key, leave the test fields empty, put the key in CITEWELL_LIVE_API_KEY, and switch to live from the Connection tab after the app starts. If you want the app to start in live mode, set CITEWELL_DEFAULT_ENVIRONMENT=live.
Then run:
npm run check
npm run devOpen:
http://localhost:4310
The browser UI only displays masked connection status. To change secrets, edit .env and restart the server. To switch between configured sandbox and live API keys, use the Environment selector in the Connection tab.
Connection values are read from .env only. If a Citewell value is missing from .env, the UI shows a warning and leaves that value empty instead of falling back to shell environment variables or browser-saved state.
| Name | Required | Description |
|---|---|---|
PORT |
No | Local Express port. Defaults to 4310. |
CITEWELL_API_BASE_URL |
Yes | Citewell API origin, for example http://localhost:8000. Do not include /api/public/v1. |
CITEWELL_DEFAULT_ENVIRONMENT |
No | Initial profile. Use test for sandbox-style API keys or live for live keys. Defaults to test, unless only the legacy CITEWELL_API_KEY is set and it starts with cwpk_live_. |
CITEWELL_TEST_API_KEY |
No | Sandbox public API key, usually starting with cwpk_test_. |
CITEWELL_TEST_WEBHOOK_SECRET |
No | Sandbox endpoint signing secret used to verify incoming webhook deliveries. |
CITEWELL_LIVE_API_KEY |
No | Live public API key, usually starting with cwpk_live_. |
CITEWELL_LIVE_WEBHOOK_SECRET |
No | Live endpoint signing secret used to verify incoming webhook deliveries. |
CITEWELL_API_KEY |
No | Legacy single-profile API key. Used for the default environment if the matching profile key is not configured. |
CITEWELL_WEBHOOK_SECRET |
No | Legacy single-profile webhook signing secret paired with CITEWELL_API_KEY. |
The app starts without credentials, but Citewell API calls return a local configuration error until CITEWELL_API_BASE_URL and an API key for the active environment are set.
Test and live use the same CITEWELL_API_BASE_URL. The selected API key decides which Citewell environment handles the request:
cwpk_test_...submits sandbox jobs and should be paired with the sandbox webhook endpoint secret.cwpk_live_...submits live jobs and should be paired with the live webhook endpoint secret.
The Environment selector changes the active API key used by browser actions only. It does not edit .env and it does not create webhook endpoints in Citewell. The selected profile is kept in server memory, so restarting the server goes back to CITEWELL_DEFAULT_ENVIRONMENT.
Webhook verification is separate from the active API key. Citewell webhook endpoints are registered per environment, and each endpoint signs with its own endpoint secret. This sample tries every configured CITEWELL_TEST_WEBHOOK_SECRET and CITEWELL_LIVE_WEBHOOK_SECRET when verifying incoming deliveries, so the same /webhooks/citewell receiver can accept either environment if both secrets are present.
Common setups:
.env setup |
Startup behavior | What to do in the UI |
|---|---|---|
Sandbox and live keys are both set, default is test |
Starts on sandbox. Both profiles are available. | Use the selector when you want to send a live request. |
Only live key is set, default is test |
Starts on sandbox and shows a missing sandbox key warning. | Switch to live before calling the API, or set the default to live. |
Only sandbox key is set, default is test |
Starts on sandbox and is ready. Live switch is disabled by validation. | No extra step unless you add a live key later. |
| No profile keys are set | App starts, but API calls fail with a local configuration error. | Add a sandbox or live key to .env, then restart. |
Only legacy CITEWELL_API_KEY is set |
The key fills the default profile. A cwpk_live_... key makes the default profile live unless explicitly overridden. |
Prefer the profile-specific variables for new work. |
- Start the app and open the browser UI.
- Confirm that
Connectionshows the.envbase URL, active environment, and masked API key. - Click
Ping,Account,Credits,Usage, andWebhook Events. - In the
Submittab, useDirect Submitto create a report job from an uploaded.pdfor.docx. - The app automatically starts tracking the active report after a successful submit or quote confirmation.
- Use
Report Controlsto fetch a report, cancel a queued or running job, recover byIdempotency-Key, or stop tracking. - Use
Quote First Flowwhen you want to request a quote before submitting. - Click
Poll Quoteuntil the quote is ready, or useRecover Quoteif the initial quote response was lost. - Open the
Reportstab to inspect polling progress and the combined polling/webhook logs. - Open the
Webhookstab to inspect the receiver path and received webhook deliveries. - Open the
Audittab to compare the report, logs, and local consistency checks.
The active report sidebar stays visible while you work. It shows report ID, status, and progress for the current tracked report.
When a report succeeds, the sample renders the returned report JSON into a Reference Verification Report preview. This is meant to confirm that the public API response contains enough data to reconstruct the result view.
The preview uses available response fields for:
- editorial decision summary
- content and format score cards
- citation context and claim-evidence details
- reference-level verification details
- annotated HTML preview, when present
- PDF download button, only when
pdf.download_urlis present
For local webhook delivery, expose the dev server through a tunnel:
https://your-public-tunnel.example -> http://localhost:4310
Register this destination in the Citewell dashboard:
https://your-public-tunnel.example/webhooks/citewell
When a matching sandbox or live webhook secret is configured, the sample verifies X-Webhook-Signature and X-Webhook-Timestamp before accepting signed events. If no webhook secret is configured, incoming webhook events are stored but marked as unverified.
Received webhook events and polling logs are in memory only. Restarting the server clears them.
Run these checks before pushing:
npm run audit:secrets
git status --ignored --shortExpected ignored local files:
.envnode_modules/.uploads/
Never commit a live cwpk_... API key, whsec_... webhook secret, private key, customer manuscript, or generated upload file.
MIT