# Syndelo Public API — v1

The public API uses the same Laravel controllers, tenant isolation, role permissions, validation, audit trail, and plan-capacity enforcement as the web app. The OpenAPI catalog is the authoritative allow-list, not every browser route.

- Human reference: /developers
- OpenAPI 3.1: /api/v1/openapi.json
- Agent discovery: /llms.txt
- Base path: /api/v1/organizations/{organization}

Use your deployment's origin. During local development the frontend proxy is http://127.0.0.1:5178 and the Laravel API origin is http://127.0.0.1:8008. Production calls require HTTPS. Do not assume a production deployment is already live.

## Create a key

1. Sign into a Growth or Scale sponsor workspace.
2. Open Settings → API Access. Creating keys requires `settings.write`; Owners and Admins have this permission by default.
3. Choose a descriptive name, Read Only or Read & Write, and expiry (1–90 days; UI presets 7, 30, 90).
4. Copy the secret once and store it in your server's secret store. The screen also shows the workspace UUID and base path.

There is a maximum of 25 active keys per user per workspace. Keys are hashed at rest. Lists never return secrets. Revoke your own keys from the same screen, including after a plan downgrade or demotion to Read Only. Keys cannot manage other keys, authentication, or billing. Shared demo sessions cannot issue or use API keys.

Each key is bound to one workspace. Access is the intersection of the key's read/write ability, current user membership and permissions, and the parent subscription plan. A write key does not grant a missing permission. Plan changes and membership removal are checked on every request. Launch is not eligible. API and MCP keys are not interchangeable. APIs do not bypass business limits or investment authorization.

## Authentication

Use server-to-server HTTP with `Authorization: Bearer <key>`. Include `Accept: application/json`; body requests also need `Content-Type: application/json`. Do not send browser session cookies or Origin/Referer headers for a server-to-server integration. Cookie-based web authentication is separate and subject to CSRF protection. Do not put keys in browser JavaScript, URLs, logs, or public AI prompts. Set these environment variables securely before running examples:

```sh
# SYNDELO_ORIGIN = your deployment origin, without a trailing slash
# WORKSPACE_ID = the UUID shown in Settings → API Access
# SYNDELO_API_KEY = the key copied once after creation
curl "$SYNDELO_ORIGIN/api/v1/organizations/$WORKSPACE_ID/contacts?per_page=25" \
  -H "Authorization: Bearer $SYNDELO_API_KEY" \
  -H "Accept: application/json"
```

## Create a contact

Requires a Read & Write key and `crm.write` permission. This changes workspace data; use an authorized test workspace for this example.

```sh
curl -X POST "$SYNDELO_ORIGIN/api/v1/organizations/$WORKSPACE_ID/contacts" \
  -H "Authorization: Bearer $SYNDELO_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"first_name":"Alex","last_name":"Example","status":"prospect"}'
```

Successful creation returns HTTP 201 with the record including its `id`. Fetch `GET /contacts/{id}` to obtain the current complete record and `version` before updating. `PATCH /contacts/{id}` requires the current version plus first_name, last_name, and status; PATCH is not necessarily a partial-field update. Stale versions return 409. All record IDs are workspace-scoped; another workspace's record returns 404.

## Supported operations

Read the OpenAPI `paths` for exact methods, request fields, required fields, permissions, and audiences. Resources include contacts, investing accounts, CRM tasks, investment vehicles, commitments, capital calls, offerings, subscriptions, payments, distributions, reports, document metadata, investor updates, signing templates and agreements, accreditation status, compliance checks, and workspace administration.

This release does not expose file uploads/downloads, billing, authentication/account-security management, key management, investor-only workflows, or sandbox simulation through public API keys. Some collections are paginated; others return the complete accessible collection. Future endpoints must be added to the shared catalog explicitly before API keys can access them.

## Formats and responses

- JSON bodies; UTF-8 strings. Dates use YYYY-MM-DD. Timestamps are ISO-8601 where supplied.
- Money inputs named `amount`, `target`, or `minimum` are USD decimal strings, e.g. "25000.00". Never use floating-point numbers for these inputs. Fields ending `_cents` in responses represent integer cents. `_bps` fields are basis points.
- Paginated endpoints return Laravel pagination objects with `data`, `current_page`, `last_page`, `per_page`, and related metadata. Contacts accept `page` and `per_page` (1–100). Only send pagination fields advertised for that endpoint. Money uses separate payment_page, distribution_page, and ledger_page parameters.
- Non-paginated lists generally return `data`; other endpoints return a record or operation-specific object. The API preserves the underlying controller's response shape; there is no universal response envelope. The OpenAPI schema currently covers requests and common error statuses, not exhaustive response models.
- Errors contain a human-readable `message`. Validation errors also contain `errors`, an object of field names to message arrays. Do not depend on exact message text for branching.

## Versions, idempotency, and external actions

Read current state before updates that require `version` or `revision`. A 409 means reload and resolve the conflict, not blindly overwrite.

Payment preparation and distribution creation require an `Idempotency-Key` HTTP header. The OpenAPI specification declares it as a header, not a JSON body property. Reuse a key only for the identical request. Agreement preparation uses a JSON `request_key` UUID. Other writes do not offer general idempotency. A timeout does not prove that a write failed; inspect the resource before repeating it.

Money endpoints retain maker/approver separation and acknowledgments. Signing, accreditation, email, and AI depend on configured providers and may have external effects. A successful local/sandbox response is not real funds movement, accreditation, or execution of an agreement. Do not infer compliance from reminders, calculations, or AI output.

## Errors and rate limits

- 400: HTTPS required or malformed request.
- 401: Missing, invalid, expired, or revoked token.
- 403: Wrong plan, token workspace/scope, membership, role, or unsupported endpoint.
- 404: Resource not found in this workspace.
- 409: Version conflict or conflicting operation.
- 422: Validation or capacity limit. Inspect `errors`; correct fields or upgrade as appropriate.
- 429: Rate limited. Wait for `Retry-After` seconds.
- 5xx: Server/provider failure. For writes, check the current record before any retry.

Rate limits are 120 requests per minute per API key and 180 per minute per user across authenticated REST requests. Endpoint-specific limits may be lower. Rotating keys does not remove the shared user limit. Use exponential backoff with jitter for safe reads and always honor Retry-After.

## Versioning

The API is namespaced `/api/v1`. Integrations should tolerate additive fields. Breaking changes require a new version or a communicated migration. This initial reference is generated from the same operation catalog used to authorize requests; request payload validation remains in the shared controllers.

## API Scope

The public API exposes customer business operations only. Registration, authentication, account security, billing, token management, team invitations, roles, workspace administration, branding, hostnames, and onboarding configuration are app-only workflows. They are excluded from the public reference and cannot be accessed using REST API tokens. New internal endpoints are excluded by default.
