# CarSoft partner integration guide

Contract: `partner-openapi.json`, version `0.3.0-draft`. This guide describes the local implementation under verification. It is not a released API or an invitation to use production dealership data. The intended API origin is `https://app.carsoft.ai`; production partner access remains disabled.

## Apply and obtain approval

Describe your organization, beneficial ownership, app, hosting, support/security contacts, data use/storage/subprocessors, retention/deletion, AI use, incident process, commercial terms, execution model and requested permissions. Declare any financial capabilities separately. Your application is a request for manual review; receipt or screening does not provide credentials or production approval.

CarSoft manually reviews each application and material revision. The approved manifest records exact public fields, scope ceiling, redirects, embedded origin and locations, webhook origins, retention exceptions and pricing. Domains must be verified through the completed enrollment process before operational credentials are issued. The present local administrative workflow does not yet provide a complete self-service developer account, domain-verification or isolated sandbox program.

Dealership administrators separately install an approved revision for one dealership, grant a subset of permissions, accept disclosed terms and configure financial limits. Partner approval alone never provides access to all dealerships. CarSoft reserves destructive actions, staff/role management, platform administration, platform subscription/security changes and credential access to the platform.

## Authorization

Use a confidential backend client. Secrets and tokens belong on your server. Browser scripts, iframes, public repositories, URLs and logs must not contain them. Public clients and client-credentials/password grants are unavailable in this draft.

1. Obtain a client ID, exact registered redirect and secret through approved credential issuance.
2. Generate a cryptographically random state and PKCE verifier. Store them as a short-lived, one-use transaction bound to the person initiating the connection. Derive the S256 challenge as base64url(SHA-256(verifier)).
3. Direct the dealership user to `/partners/authorize` with `response_type=code`, `client_id`, exact `redirect_uri`, space-separated `scope`, `state`, `code_challenge` and `code_challenge_method=S256`.
4. The user reviews access and reauthenticates. Interactive execution follows their current permissions; only a dealership administrator can authorize background execution under the installation's grant.
5. On your registered callback, compare state to your stored transaction before exchanging the code. Never accept an unsolicited callback or reuse state.
6. POST form-encoded `grant_type=authorization_code`, `client_id`, `client_secret`, `code`, `redirect_uri` and `code_verifier` to `/api/partners/oauth/token`.
7. Store returned tokens in protected server storage. Send `Authorization: Bearer <access_token>` to the resource API.

Authorization codes last at most 120 seconds and redeem once. Access tokens last at most 15 minutes; use the returned `expires_in`. Refresh tokens last at most 30 days and rotate on every successful refresh. POST `grant_type=refresh_token`, `client_id`, `client_secret` and the current `refresh_token`; optionally request a reduced scope set. Atomically replace stored tokens and serialize refresh per connection. Reuse of an already consumed refresh token revokes its entire family. A lost refresh response requires a new authorization flow rather than repeated replay.

Credential rotation immediately invalidates the old secret, pending codes and existing client token families. Revocation is permanent. Dealer consent/configuration changes also revoke existing tokens and pending codes, disable subscriptions and cancel unsubmitted financial requests. Request fresh authorization and recreate subscriptions. Changes to an installed app revision require dealership-admin consent; new permissions start unselected in the review UI. Neither a review nor a pricing change silently expands access.

On `401`, resolve expiry/revocation through a valid refresh or fresh authorization. On `403`, obtain the missing approval/consent rather than retrying with guessed identifiers. Partner credentials do not replace CarSoft browser sessions.

## Resource requests

The implemented resource paths are `/api/partners/v1/inventory`, `/leads`, `/clients`, `/service`, `/parts` and `/appointments`, with individual record routes at `/{id}`. Each operation requires its `resource:read`, `resource:create` or `resource:update` permission. The OpenAPI artifact is authoritative for accepted fields and required values. Read projections include only approved public fields plus ID/version; write permissions do not authorize hidden fields.

GET lists accept only `limit` (1–100, default 25) and `after`. Follow `pagination.next`; do not assume a complete export or stable snapshot. Reads return versions as `partner_revision` and ETags for individual records.

POST creates require an `Idempotency-Key` of 8–128 characters from letters, digits, `.`, `_`, `:`, `-`. Generate it once per intended effect and store it before sending. Retry the exact intent with the same key after a lost response. Reusing a key with different content returns `409`. PATCH also requires `If-Match: "<current version>"`. A stale version returns `409`; reread and resolve the change rather than forcing an overwrite. No resource DELETE is provided.

CarSoft allocates inventory stock codes, vehicle titles and parts SKU codes. Do not invent these fields. Dates, text lengths, enumerations, monetary representations and relationship identifiers are validated; fields outside the approved policy are rejected. Tenant identity never comes from your payload.

Current local throttles are 600 requests/app/minute and 120 requests/installation/minute, with a separate ingress limit. `429` carries `Retry-After: 60`. These are implementation defaults under verification, not a public availability commitment. Use bounded backoff, concurrency limits and per-installation queues.

## Customer invoices

`invoices:read` permits customer-facing invoice projections. `invoices:create` creates customer-only drafts for existing authorized customers. Send `clientId`, `department` (`Vehicles`, `Service`, `Parts`) and 1–100 lines. Lines use `kind` (`Labor`, `Part`, `Fee`, `Discount`, `Tax`), `name`, positive `qty` (at most three decimal places) and nonnegative integer `unitAmountMinor`. USD is the current implemented currency. Aggregate totals are rounded once to minor units; discount lines reduce the total.

CarSoft derives the customer name and allocates an `APP-` number in a separate namespace. Apps cannot supply receipts, notes, arbitrary payer classifications, internal/warranty lines or merchant secrets. `invoices:draft-update` replaces the customer/department/lines of a draft created by that same installation, using the current ETag and an idempotency key. Sent, paid, voided or payment-linked drafts are locked. Financial adjustments and dispute interfaces are not available merely because invoice reads are available.

## Financial requests

Ordinary apps request financial actions through CarSoft; only independently reviewed processor adapters connect to merchant providers. Funds go directly through the dealership's merchant provider. Partners collect their own app fees and CarSoft separately bills partners under approved commercial agreements. Installing an app does not create a CarSoft-collected partner subscription.

Each financial capability is separately granted. POST `/api/partners/v1/payments/links`, `/charges` or `/refunds` requires the matching scope, an idempotency key, `invoiceId`, reviewed `processorId`, positive integer `amountMinor` and `currency: "usd"`. Charges additionally require an authorized `savedMethodId`; refunds require the original confirmed `parentId`.

A saved method is a server-protected reference backed by documented customer authorization and amount/currency restrictions. Apps never receive raw card/bank details or merchant keys. An API request cannot supply consent on behalf of the customer. Actual customer setup/onboarding and provider acceptance remain incomplete in this draft.

A payment intent is stable across retries. The response reports its state, not a claim that funds have settled. Payment links must stay on the processor’s explicitly reviewed public HTTPS origins. `reserved` means not submitted; `submitting`, `pending`, `requires_action` or `unknown` may involve a provider effect. Use the same intent/key and status lookup; never create a replacement charge to resolve a timeout. `succeeded` requires a verified merchant/intent/invoice/amount/currency/provider identity and a durable outcome. `failed` is terminal only when supported by verified provider state. `approval_required` returns HTTP 202 for one exact refund awaiting dealership-admin decision.

Automatic refunds obey per-refund and cumulative daily limits and the original payment's remaining refundable amount. Splitting refunds does not bypass the cumulative limit. Above-limit approvals name an exact immutable refund, expire after 15 minutes and are rechecked at submission. Submission-day quotas also apply to requests reserved on an earlier day. Saved-method charges recheck current customer consent, merchant connection, processor status, app access and current actor permissions before submission.

GET `/api/partners/v1/payments/{id}` with `payments:read` returns only this installation's financial intent. POST `/{id}/cancel` with `{ "operation": "link" | "charge" | "refund" }` and the corresponding action permission can cancel an unsubmitted reserved/approval-required request. Repeating cancellation is safe. Already submitted effects cannot be canceled with this endpoint; reconciliation/refund flows apply. It does not delete invoices or records.

Processor adapters and callbacks have not completed sandbox acceptance. Additional currencies, submitted-payment cancellation/expiry, authentication-required recovery, adjustments, disputes and full saved-method onboarding remain incomplete. Do not infer support from the generic payment state names.

## Events

POST `/api/partners/events/subscriptions` with `events:subscribe`, each requested resource's read permission, an exact reviewed HTTPS endpoint and a unique resource list. Resources include the six operational resources plus `invoices` and `payments`. The one-time `signingSecret` belongs in protected server storage. Subscriptions deliver only future events. Financial payment events belong to their originating installation; no merchant secrets or payment tokens are included.

Delivery is at least once; ordering is not guaranteed. An envelope has string `id`, `version: "1"`, `type: "<resource>.<created|updated>"`, `occurredAt` and `data: { resource, recordId, revision }`. Treat the event as a reference and fetch the record through the authorized API. Use the record revision to avoid overwriting newer local state.

Validate `CarSoft-Signature: t=<Unix seconds>,n=<nonce>,v1=<hex HMAC>` against the exact raw UTF-8 body. Compute HMAC-SHA256(signingSecret, `timestamp + "." + nonce + "." + rawBody`) and compare in constant time. Reject timestamps more than five minutes from your synchronized clock. Persist/deduplicate the event ID atomically before applying effects; do not rely on timestamp validation alone. Enqueue durably before returning 2xx. Avoid holding delivery open for long processing.

GET the subscription collection for bounded current subscriptions; GET `/{id}` for up to 100 delivery summaries. PATCH `/{id}` with `{ "action": "disable" }` or `{ "action": "rotate-secret" }`. Rotation immediately replaces the signing secret and additionally requires current read access to the subscribed resources. Use a controlled transition and durable deduplication. Automated replay and full operational recovery controls remain incomplete.

CarSoft checks current permissions before delivery and retries; eight attempts lead to a dead state. HTTPS destinations resolve exclusively to public addresses, are pinned for each request, and redirects are not followed. DNS, connection/absolute durations and response size are bounded. Revocation cannot retract previously received events.

## Isolated embedded UI

The currently implemented location is the dedicated Apps area. Other declared locations are still pending. Your reviewed UI origin must be separate from the entire `carsoft.ai` cookie site. Partner scripts do not run in the host page. The iframe permits scripts, forms and its own origin while restricting host navigation, popups and sensitive browser features.

The current bridge accepts only `{ "type": "carsoft.context", "requestId": "<1–64 letters/digits/underscore/hyphen>" }`. The host checks exact sender origin and frame window identity. The response contains app/installation IDs and locale, never host session credentials or broad API tokens. IDs are context, not authorization. Your backend still needs its approved OAuth connection. Do not assume navigation/download/clipboard/financial host actions or a complete extension SDK.

## Disconnection, data use and support

Disconnect/suspension prevents new partner actions. Unsubmitted financial work is canceled; submitted external effects still reconcile internally without restoring app access. Retain required transaction identities for that reconciliation under the reviewed policy. A processor switch must not abandon old transactions.

Use data only for the approved purpose and permitted retention. Delete partner-held data, including subprocessors/copies, within 30 days after disconnection, subject only to specifically reviewed retention exceptions. Provide a deletion attestation and evidence when requested. API revocation cannot prove deletion in your systems.

Partner support handles app defects; CarSoft handles platform failures, access enforcement and suspensions. Include the `X-Request-ID` and structured error code in a support report without tokens, secrets, raw payment data or unrelated customer records. Rotate/revoke compromised credentials immediately and follow the approved incident process.

## Release and verification boundary

This guide accompanies a draft artifact and synthetic local verification. It does not establish a complete developer program, production deployment, sandbox credentials, provider acceptance, independent security assessment, operational service promise or native-app compatibility. CarSoft's launch acceptance matrix must be completed before production partner access is offered.
