Sandbox and code
A working implementation of this contract runs on this domain. Use it to develop against before your own feed exists, and to compare responses when something does not line up.
The sandbox feed
Base URL: https://upwolf.com/dev/sandbox/feed
Token: sandbox-demo-token
— send it in the Authorization header, or leave it out entirely. A wrong token answers 401, so you can test that path too.
| Try | What it shows |
|---|---|
| offers | Everything live right now |
| offers/DEMO-1001 | A fully populated offer — every recommended field present |
| offers/DEMO-1002 | The bare minimum — only required fields, so you can see the degradation |
| offers/DEMO-1003 | A closed offer that still answers |
| offers/NOPE | Unknown offer — answers 404 with an error body |
| offers?updated_since=2026-08-01T00:00:00Z | Change list |
| campaign-candidates | Campaign candidates, one active and one cancelled |
| offers/DEMO-1001/applications-stats?from=2026-09-01T00:00:00Z | Response counts |
| ?upwolf_route=offers/DEMO-1001 | The query route style, for hosting that drops PATH_INFO |
Reference implementation
One PHP file, no dependencies, no framework. It handles both route styles, bearer auth and the error shapes; what it does not have is your database. Three functions are marked TODO — fill them in and the feed is done.
Download feed-upwolf.php · view in browser
shell
curl -s https://upwolf.com/assets/dev/reference-feed.php.txt -o feed-upwolf.php
One trap worth knowing about:
on CGI and FastCGI hosting the Authorization header never reaches PHP. If your token always looks missing, that is why — the reference file shows the .htaccess lines that fix it.
Before you tell us it is ready
- The `offers` snapshot returns everything that is live right now, and nothing else.
- `version` changes on every content change — and does not change when the content did not.
- The detail endpoint still answers for offers with status `closed`.
- An unknown offer returns `404` with an `{"error": {...}}` body, not `200` with an empty object.
- A cancelled candidate carries `status: "cancelled"` instead of disappearing from the list.
- You send `locations` as an empty array when the offer has no city.
- `count` in the response stats is an integer, not a string.
- You have documented whether the `updated_since` boundary is inclusive and whether ordering is stable.
- The validator at `/dev/validator` comes back without a red line.
Run the validator against your feed — it checks the same list, plus the response shapes.