# Hella Partner API — coding-agent context

Contract version: 1.0.0-beta.1
Canonical source: `openapi/partner-api.v1.yaml`
Base URL: `https://www.app.hellastays.com/app/api`

## Allowed operations

- `listPartnerProperties`: GET `/v1/properties` — required scope `properties:read`
- `getPartnerProperty`: GET `/v1/properties/{propertyId}` — required scope `properties:read`
- `getPartnerPropertyAvailability`: GET `/v1/properties/{propertyId}/availability` — required scope `availability:read`
- `createPartnerQuote`: POST `/v1/quotes` — required scope `quotes:write`

## Stable problem codes

- `VALIDATION_ERROR`
- `INVALID_JSON`
- `PAYLOAD_TOO_LARGE`
- `PARTNER_AUTH_REQUIRED`
- `INVALID_PARTNER_CREDENTIAL`
- `PARTNER_ACCESS_REVOKED`
- `PARTNER_SCOPE_INSUFFICIENT`
- `ORIGIN_NOT_ALLOWED`
- `PROPERTY_NOT_FOUND`
- `STAY_UNAVAILABLE`
- `QUOTE_UNAVAILABLE`
- `RATE_LIMIT_EXCEEDED`
- `PARTNER_API_UNAVAILABLE`
- `PARTNER_OPERATION_NOT_FOUND`
- `HANDOFF_INVALID`
- `INTERNAL_ERROR`

## Non-negotiable integration rules

1. Treat `propertyId` and `quoteId` as opaque strings.
2. Send the Bearer partner credential only from a trusted server-side environment.
3. Never infer an exact location; the API intentionally returns only city, country and nullable approximate area.
4. Validate real Gregorian dates and require `checkOut > checkIn`. A stay is `[checkIn, checkOut)` in the property timezone, with at most 60 occupied nights.
5. Treat top-level availability as the whole-stay authority: `available: true` requires one eligible inventory type for every night. Never combine per-day capacity across types.
6. Preserve the server's deterministic `propertyId` order when consuming paginated search results.
7. Treat all monetary fields as decimal strings in `currency`; do not parse them through binary floating-point for accounting.
8. Branch on RFC 9457 `code`, never on `title`, `detail` or translated text. Log `correlationId` for support.
9. Traffic has an ingress limit of 300 requests per 60 seconds per source IP and a default authenticated quota of 120 requests per 60 seconds per partner credential. Credential rate-limit headers are authoritative for that partner quota.
10. On 429, honour `Retry-After` and use exponential backoff. Rate-limit headers are exposed through CORS.
11. A quote is indicative. `pricing.pricePreservedInHandoff` is false, so show that the final price is revalidated.
12. Use the returned absolute `handoffUrl` unchanged. Do not construct it, append a return target, or decode its signed partner reference.
13. Do not send personal data or unsupported fields in quote requests. Unknown properties return 404.
14. Treat `PARTNER_OPERATION_NOT_FOUND` as an unknown API operation and `HANDOFF_INVALID` as an invalid, expired or tampered handoff reference.
15. Generate client behavior from OpenAPI. Do not add endpoints that are absent from this contract.
16. **PAYMENT_DATA_FORBIDDEN** — Never send payment card data, bank-account data, payment-method identifiers, payment tokens, CVC/CVV values or any other payment credential to Partner API v1. Partner API v1 exposes no payment or booking operation.
17. **QUOTE_AUTO_RETRY_FORBIDDEN** — Do not automatically retry `POST /v1/quotes`. Quote creation is non-idempotent and this version accepts no idempotency key. A retry can create a new `quoteId`; retry only after an explicit caller decision.
18. **PRODUCTION_LOAD_TESTING_FORBIDDEN** — Do not run load, stress, soak, fuzz or high-concurrency tests against the production base URL. Use the generated mock or an explicitly authorised non-production onboarding environment.

## Executable sequence

Run `generated/partner-api/quickstart.mjs`. It generates future dates, searches, handles zero results, gets detail, checks availability, creates a quote and uses the returned `handoffUrl`. For CI, run it against `generated/partner-api/mock-server.mjs` and the synthetic fixture.
