C2C Public API (v1)

C2C Public API (v1)

Overview

The C2C (tenant public) API is a versioned, JSON API scoped per tenant. All routes under /api/c2c share the same authentication, API version validation, and request logging.

Base URL pattern:

https://{your-tenant-host}/api/c2c

Example: https://mytenant.click2close.io/api/c2c

Full endpoint URLs are base URL + path, e.g. POST https://{tenant-host}/api/c2c/contacts/assets.


Required headers (all C2C endpoints)

Header

Required

Description

Header

Required

Description

X-Api-Version

Yes

Must be v1 (case-insensitive). Missing or empty → 400 missing_api_version. Unsupported version → 400 unsupported_api_version.

API key

Yes

See Authentication. Missing or invalid → 401 invalid_api_key.

Content-Type

Yes (for JSON bodies)

Must be application/json for endpoints that accept a body.


Authentication

Provide exactly one of:

  1. Authorization: Bearer c2c_{secret} — full key including the c2c_ prefix.

  2. X-Api-Key: c2c_{secret} — same string as the Bearer token.

API keys are tenant-scoped and issued in the product. The plaintext key must start with c2c_.

Available endpoints

Method

Path

Description

Method

Path

Description

GET

/api/c2c/health

Health check for the C2C stack on this tenant.

POST

/api/c2c/contacts/assets

Attach one or more base64-encoded files to an existing contact (identified by email).


GET /api/c2c/health

Success: HTTP 200

{ "data": { "status": "ok" } }

Use this to verify DNS, TLS, X-Api-Version, and API key before heavier integrations.


POST /api/c2c/contacts/assets

Uploads files as base64, decodes them on the server, and stores them as ** Media Library** assets on the Contact.

Purpose

  • Target contact must already exist (matched by email).

  • Attach 1–10 files per request; each file must pass MIME allowlist, size limits, and content-vs-declared-MIME checks.

Request

Headers

  • X-Api-Version: v1

  • Content-Type: application/json

  • Authorization: Bearer c2c_… or X-Api-Key: c2c_…

Body (JSON)

Field

Type

Required

Description

Field

Type

Required

Description

email

string

Yes

Contact email. Lookup is case-insensitive (trim + lowercase comparison). Contact must exist and not be soft-deleted.

files

array

Yes

Min 1, max 10 objects.

Each element of files

Field

Type

Required

Description

Field

Type

Required

Description

filename

string

Yes

Max 255 characters. Logical file name (basename used if a path is given).

mime_type

string

Yes

Must be one of the allowed MIME types listed below. Compared to detected MIME from decoded bytes (finfo); mismatches fail unless an allowed alias applies.

content

string

Yes

File bytes base64-encoded. Optional data URI prefix supported: data:...;base64,<payload> — only the segment after the last comma is decoded.

alt

string

No

Max 255. Optional accessibility text stored on the media record.

description

string

No

Max 1000. Optional description stored on the media record.

Allowed mime_type values

  • Images: image/jpeg, image/png, image/webp, image/gif, image/svg+xml

  • PDF: application/pdf

  • Spreadsheets / CSV: text/csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

  • Plain text: text/plain

Size limits

Limit

Value

Limit

Value

Per file (decoded binary)

≤ 25 MB

Total across all files in one request

≤ 50 MB

MIME aliases

The server may accept a declared mime_type if detected MIME matches known aliases (for example: XLS/XLSX sometimes detected as application/octet-stream; CSV as text/plain). Exact equality between declared and detected MIME also passes.

Processing flow

  1. Validate JSON and custom rules (base64, sizes, MIME allowlist, MIME vs content).

  2. Resolve Contact with LOWER(email) = lower(trim(email)). If none → 422 contact_not_found.

  3. For each file in array order:

    • Decode base64 → write temp file.

    • Raster images (not SVG) whose original extension is not gif are converted to WebP before save (aligned with internal media upload behavior). GIF and SVG are not converted through that path.

    • Attach media to contact default collection, public on tenant disk.

  4. Return 201 with contact + created asset metadata.

  5. Unexpected server errors during upload → 500 store_failed (logged server-side).

Success response

HTTP 201 Created

{ "data": { "contact": { "uuid": "<contact_uuid>", "email": "<email_as_stored>" }, "assets": [ { "uuid": "<media_uuid>", "name": "<stored_name>", "mime_type": "<mime_type>", "type": "image|file", "size": 12345, "created_at": "YYYY-mm-dd HH:ii:ss" } ] } }
  • type: image if stored MIME is under image/*, otherwise file.

  • name: May differ from request filename (e.g. WebP conversion changes extension for eligible images).

Error responses

Standard envelope:

{ "error": "<code>", "message": "<message>" }

Validation (422):

{ "error": "validation_failed", "message": "<message>", "errors": {} }

HTTP

error

Typical cause

HTTP

error

Typical cause

400

missing_api_version

X-Api-Version missing or empty

400

unsupported_api_version

Version other than v1

401

invalid_api_key

Missing/invalid key or wrong prefix

422

validation_failed

Schema, base64, size, MIME allowlist, or MIME/content mismatch

422

contact_not_found

No contact for given email

500

store_failed

Unexpected error while storing media

User-visible message values may be localized (en / es) depending on server configuration (see resources/lang/*/api_error.php, key c2c_contact_asset).

Example (curl)

curl --request POST \ --url 'https://{tenant-host}/api/c2c/contacts/assets' \ --header 'Authorization: Bearer c2c_YOUR_KEY' \ --header 'Content-Type: application/json' \ --header 'X-Api-Version: v1' \ --data '{ "email": "user@example.com", "files": [ { "filename": "document.pdf", "mime_type": "application/pdf", "content": "<base64_here>" } ] }'

Integrator notes

  • The API does not create contacts; ensure the contact exists first.

  • Each successful call adds media rows; it does not replace previous uploads unless handled elsewhere in your stack.

  • All C2C routes are under /api/c2c/ on your tenant API host.