# CarSoft partner integration guide

Contract: [partner-openapi.json](partner-openapi.json), version `0.38.0-draft`. Implementation is prepared locally; production release and activation are pending. Intended origin: `https://app.carsoft.ai`. Documentation does not grant access or provide sandbox credentials. The OpenAPI download defines exact request fields, responses, scopes and limits.

## Start with an application

Apply through the CarSoft Website partner application form. Describe your organization, owners, integration, requested access, support/security contacts, data use, retention and commercial model. The form uses CAPTCHA and an idempotency key. A submitted receipt means your application was saved for manual review; it creates no developer organization, API credential, approved app or dealership access. Keep the receipt when contacting CarSoft.

The intake endpoint is POST `/api/partners/applications`, separate from the resource API. It accepts only configured Website origins and a CAPTCHA with action `partner_application` and the exact Website hostname. A repeat with the same key and answers returns the original receipt; changed answers conflict. A timeout is unknown, so retain the original intent when checking/retrying. The Website cannot submit when required connection/security configuration is absent.

CarSoft's platform administrator manually reviews the company and each app, exact permissions, public fields, domains, data/retention policy and pricing. Application approval, app approval, publication, credential issuance and dealership installation are separate decisions. Rejections or requests for more information do not grant access.

## Developer account and first app

Use `/partners/developer/login` after enrollment opens. Developer accounts are separate from dealership login. Confirm your email and configure TOTP. Organization membership must be active; sensitive changes require recent TOTP verification. The portal uses an opaque HTTP-only session, while `/api/partners/developer/*` uses the documented verified developer identity. An app's OAuth access token cannot administer its developer organization.

GET `/api/partners/developer/me` returns accessible organizations. Register an app through GET/POST `/api/partners/developer/apps`, then submit its manifest through GET/POST `/api/partners/developer/submissions`. Supply only the documented partner-owned fields and exact current revision/request identifiers. Private reviewer evidence and approval status cannot be supplied by partners. Registration creates a draft, not a published app.

Use `/api/partners/developer/domains` to request and verify ownership of your exact redirect, event and embedded domains. Pending, expired or revoked proofs grant no authority. Renew ownership proofs before expiry. A verified domain does not itself approve an app or its permissions.

The manifest declares requested scopes/public fields, description, data use, retention/exceptions, pricing, redirects, webhook origins and optional embedded origin/locations. Material changes need platform review and fresh dealership consent. Previously installed dealerships retain their accepted revision until they approve the update. New permissions start unchecked.

Team roles, invitations and credential sharing are available through the documented `/members`, `/invitations`, `/invitations/accept`, `/sharing` and `/ownership` routes. Organization-owner transfers and credential sharing require exact identity/custody review; an invitation is not automatic ownership. Do not share a developer login or place secrets in application/review text. Lost-authenticator recovery goes through manual platform review; it never bypasses verified membership.

## Dealership installation and OAuth

A dealership administrator connects an approved, published app from `/apps`. They review partner pricing, data use, retention, approved fields and requested permissions. Partners collect their own app/service fees. CarSoft does not distribute partner funds. Staff can use only the capabilities permitted by their current dealership role.

1. Obtain an issued client ID and client secret for the approved app revision. Keep the secret on your server.
2. Generate a random `state` and PKCE verifier; derive an S256 challenge.
3. Direct the signed-in dealership user to `/partners/authorize` with `client_id`, exact registered `redirect_uri`, `response_type=code`, space-separated `scope`, `state`, `code_challenge` and `code_challenge_method=S256`.
4. The requested scopes must already be installed. Interactive authorization remains bound to the user's current permissions; only dealership administrators can authorize background execution.
5. Validate the callback's `state`. Exchange the single-use code by POSTing form-encoded `grant_type=authorization_code`, `client_id`, `client_secret`, `code`, `redirect_uri` and `code_verifier` to `/api/partners/oauth/token`.
6. Store returned tokens securely on your server. Send `Authorization: Bearer <access_token>` to the resource API.

Codes last at most 120 seconds; access tokens at most 15 minutes. Honor `expires_in`. Refresh tokens last at most 30 days and rotate on successful refresh. Exchange the current refresh token with `grant_type=refresh_token`, client ID/secret and `refresh_token`; an optional scope set can reduce access. Serialize refresh and atomically replace tokens. Reuse of a consumed refresh token revokes the family. A lost refresh response requires fresh authorization rather than repeated replay.

The `/api/partners/developer/credentials` routes manage server credentials under reviewed organization permissions. Rotation invalidates the old secret, pending codes and token families; revocation is permanent. Consent changes and disconnection revoke existing authorization, disable subscriptions and cancel unsubmitted financial work. Neither installation nor a commercial agreement silently expands access.

## Resource API

Operational resources are `inventory`, `leads`, `clients`, `service`, `parts` and `appointments`. GET/POST `/api/partners/v1/{resource}` lists/creates permitted records; GET/PATCH `/{resource}/{id}` reads/updates one record. Each operation needs its fixed scope, approved public field policy, dealership consent and current actor permission. An identifier is never authorization. Cross-dealership records are unavailable.

Lists use documented bounded keyset pagination; follow `nextCursor`. Select only approved fields. Creates use server-issued IDs; updates require the current ETag/`If-Match` and an `Idempotency-Key`. On a version conflict, read the record and reconcile before submitting a new intent. The same key/body recovers the original result; changed bodies conflict. Do not retry a timeout with a new key merely to force another write.

```sh
curl 'https://app.carsoft.ai/api/partners/v1/clients?limit=25' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```

Use protected environment variables in real integrations; never copy credentials into logs or support tickets. The example works only after release, approval, installation and authorization.

Native data deletion, dealership deletion, staff/platform administration, platform subscription/security changes, private documents and credentials are unavailable to apps. Unknown/private fields are rejected rather than silently written.

Standard failures include `401` invalid/expired authorization, `403` missing access, `409` version/idempotency conflict, `422` invalid input, `428` missing version, `429` quota and `503` unavailable/disabled service. Resource errors have `error.code`, a safe message and request ID. Honor `Retry-After`; report the `X-Request-ID` without sensitive data. OAuth failures use OAuth error names.

## Invoice drafts and payment boundary

Invoice access has separate scopes. GET `/api/partners/v1/invoices` and `/{id}` returns safe customer invoice information. POST creates a draft for an existing customer using `clientId`, department (`Vehicles`, `Service`, `Parts`) and 1–100 documented lines. Use positive quantity and integer currency minor-unit amounts. CarSoft derives customer identity, totals and a separate `APP-` invoice number. Draft updates require `invoices:draft-update`, the current version and an idempotency key; only the originating installation's editable draft can change. Sent/paid/voided/payment-linked drafts are locked. Apps cannot supply payment receipts or merchant secrets.

Stripe is the current integration. Existing native dealership Stripe payments remain separate from partner API activation. Additional payment-provider onboarding is outside this marketplace offering; a future exclusive provider requires a separate reviewed integration. Partner payment endpoints are unavailable until CarSoft activates a separately reviewed payment release. Publication or invoice-read approval does not activate payments.

For an activated payment release, POST `/api/partners/v1/payments/links`, `/charges` or `/refunds` needs the matching independently approved scope, idempotency key, invoice, reviewed processor, positive integer `amountMinor` and `currency: "usd"`. Saved-method charges additionally need the protected `savedMethodId`; refunds name the original confirmed `parentId`. Apps never receive raw card/bank details, provider keys or customer method tokens and cannot assert customer consent.

Every request rechecks merchant/account/mode, supported methods, installation, actor and customer consent. Charge/refund limits apply cumulatively, so splitting requests does not bypass them. Above-limit refunds require an exact dealership-admin approval, expire after 15 minutes and are rechecked before submission. Sensitive payment permissions/approvals require current password and session-bound MFA.

A stable intent and its reported state are not proof of settlement. `reserved` is unsubmitted; `submitting`, `pending`, `requires_action` and `unknown` may already involve a provider effect. Retrieve GET `/api/partners/v1/payments/{id}` with `payments:read`; never create a replacement charge after a timeout. Verified provider identity, merchant, invoice, amount and currency are required before recording success. GET `/payments/authorizations` returns only permitted opaque consent references. POST `/{id}/cancel` cancels eligible unsubmitted work, not an already submitted payment. Submitted effects still reconcile after disconnection/shutdown without reopening partner access.

## Events

GET/POST `/api/partners/events/subscriptions` manages subscriptions. Creation needs `events:subscribe`, every requested resource's read scope and an exact approved HTTPS destination. Keep the one-time signing secret in protected server storage. GET `/{id}` returns bounded delivery summaries; PATCH supports `disable` or `rotate-secret`. Rotation requires current resource access and invalidates the previous secret.

Delivery is at least once and ordering is not guaranteed. The envelope contains `id`, `version: "1"`, `type: "<resource>.<created|updated>"`, `occurredAt` and `data: { resource, recordId, revision }`. Fetch the authorized record; event references grant no additional fields or access. Payments belong to their originating installation.

Verify `CarSoft-Signature: t=<Unix seconds>,n=<nonce>,v1=<hex HMAC>` against the exact raw UTF-8 body. Compute HMAC-SHA256 with the signing secret over `timestamp + "." + nonce + "." + rawBody`, compare in constant time and reject timestamps outside five minutes of your synchronized clock. Atomically deduplicate the stable event ID before applying an effect; durably enqueue before returning 2xx.

CarSoft checks current access at delivery/retry, permits public HTTPS destinations only, pins resolved addresses and refuses redirects. Eight unsuccessful attempts become dead. Revocation cannot retract already received data. Scheduled bounded delivery is prepared; actual production scheduler operation is verified during release. Automatic partner replay is not offered by this contract.

## Exports, bulk and vehicle photos

POST `/api/partners/v1/exports` needs `exports:create` and resource read access. Supply `resource`, `limit` (1–500, default 100) and optional `after`. It captures only approved operational fields, up to 2 MiB. Five new captures per installation per minute are allowed. GET `/jobs/{id}` returns metadata; GET `/jobs/{id}/download` returns an authenticated no-store JSONL attachment. Follow `X-CarSoft-Next-Cursor` for another capture. Separate captures do not guarantee a whole-dataset point-in-time snapshot.

POST `/api/partners/v1/bulk` accepts 1–25 operational create/update items, up to 64 KiB. It needs the resource's bulk permission and each action permission. Whole-body validation precedes writes; individual successful items persist and conflicts return safe per-item results. Repeating the same key/body recovers results rather than repeating successful writes. Ten new bulk jobs per installation per minute and ordinary request quotas apply.

Artifacts are bound to the creating installation, actor, consent version and refresh family. Download checks access again; expiry, revocation and consent changes deny access. PATCH `/jobs/{id}` with `action: "cancel"` clears the result but does not undo completed writes. Expired results are denied immediately. Authenticated bounded maintenance clears expired payloads even during marketplace shutdown; audit/idempotency metadata remains. Larger processing engines are outside this offering.

GET `/api/partners/v1/inventory/{id}/photos` and its photo `/{photoId}/download` require `media:read`, inventory read and current record/actor access. Only the existing vehicle marketing gallery is available, at most 25 references. Downloads return re-encoded, metadata-stripped JPEGs up to 1600 pixels with no reusable public storage URL. Input/provider/processing sizes and deadlines are bounded; corrupt/unsupported content is unavailable. Arbitrary documents, identity/financial records, photo uploads and deletion are not offered. Already downloaded bytes cannot be recalled.

## Isolated embedded apps

Approved locations are `apps`, `vehicle-tab`, `customer-tab`, `dashboard-widget` and `record-action`. Your origin must be outside the entire `carsoft.ai` cookie site. CarSoft loads partner content in an isolated iframe; host navigation, popups, downloads, camera, microphone, geolocation, payment and clipboard privileges are unavailable. Native pages remain usable when an extension fails.

The only bridge request is `{ "type": "carsoft.context", "requestId": "<1–64 letters/digits/underscore/hyphen>" }`. The host checks exact sender origin/window and current server authorization before replying. Responses contain app/installation IDs, locale, location and an optional authorized inventory/client record ID; no tokens, host cookies, customer attributes or payment secrets. Record placements require current record, field and viewer permissions. Apps/widgets have no record context.

```js
const hostOrigin = 'https://app.carsoft.ai';
const requestId = crypto.randomUUID();
window.addEventListener('message', event => {
  if (event.origin !== hostOrigin || event.source !== window.parent) return;
  if (event.data?.type !== 'carsoft.context.response' || event.data.requestId !== requestId) return;
  // IDs are context; your backend still needs its approved OAuth connection.
});
window.parent.postMessage({type: 'carsoft.context', requestId}, hostOrigin);
```

One frame request can be in flight, at most one per second; the viewer has an aggregate 60 checks per minute. Changed/unavailable authorization removes the frame. Host mutation commands are rejected. Interactive record actions cannot substitute background authority. Browser evidence does not establish compatibility with every signed native client.

## Partner fees, overdue invoices and cancellation

CarSoft separately bills partners through its current Stripe account. Individual agreements define base/API/installation/revenue-share charges, spending limits, currency, tax, due dates, proration and cancellation. A partner owner with recent MFA accepts the exact reviewed fingerprint through `/api/partners/developer/billing/agreements`. Acceptance creates no automatic dealership permission or payment-provider access.

`/billing/account` handles verified billing contact consent and reviewed dedicated Stripe customer setup. `/billing/periods` exposes completed immutable monthly calculations; `/billing/invoices` exposes safe invoice status/amounts and approved hosted payment links. Monetary calculation/projection fields documented as strings preserve exact minor-unit amounts; do not convert large values through JavaScript floating-point arithmetic. Invoice credits and cash payments remain distinct.

After accepted pricing and billing-account review, prepared release-gated workers process completed UTC months, persist provider drafts, recover the original idempotent operation and reconcile verified invoices. Missing revenue/tax/account review blocks the month for administrator attention. Existing Stripe sandbox evidence covers partner fee billing; it does not imply hosted acceptance of every marketplace payment method.

Overdue invoices create platform-admin dashboard notices and email to CarSoft's existing admin alert address. Access consequences always await the platform administrator's explicit decision. There is no automatic overdue suspension or cancellation.

GET/POST `/api/partners/developer/billing/cancellation` is reserved to the current organization owner with recent TOTP. Supply the exact accepted agreement fingerprint, one request ID, reason and `confirmation=cancel`. `period_end` ends access at the next UTC month boundary; `immediate_prorated` ends access immediately with the agreed final fee calculation. Already owed invoices remain payable. Eligible members retain billing reads; revoked members do not. Reinstatement needs separate platform review.

## Disconnection, retention and support

A dealership administrator can review permissions, explicitly approve an update or disconnect from `/apps`. These decisions never delete the dealership or its native business records. Disconnection/suspension prevents new app actions; submitted financial effects remain trackable under the protected original records.

Use data only for the approved purpose and retention period. Delete partner-held data, copies and subprocessors within 30 days after disconnection, except specifically approved retention obligations. Provide the requested deletion attestation; API revocation does not prove deletion in your systems.

Partner support handles partner app/service issues; CarSoft handles platform/API access and review. The catalog shows the partner's support contact. Include request IDs and safe error codes in escalation, never secrets or unrelated customer records. Rotate/revoke compromised credentials immediately and follow the reviewed incident policy.

## Release status

This is the final simplified implementation contract prepared for review, not a claim of live release. The API, intake, developer portal, worker schedules and billing require their production configuration and explicit release approval. No general payment-provider program, paid hosted developer sandbox, larger job engine or additional exceptional accounting automation is promised. Production activation and its smoke verification are recorded separately from local fixture/provider-sandbox evidence.
