Leads from partner agencies

How a partner's marketing agency submits leads directly as the partner's prospects and immediately gets a personal booking link for each one: opt-in on its own landing page, then book an appointment without entering anything again.

The flow

The agency works with a partner key that is bound to exactly one partner (affiliate). It reads the form definition once, submits the values for each lead and gets, in the same response, the prospect's id and, with an appointment type, the partner's personal booking link. Whoever opens the link finds their details already filled in and only picks a time. The appointment belongs to the partner and their team.

1. Issue a partner key

The partner issues it in the partner portal under “Interface for my agency”, or an admin of the organization in the affiliate management on the partner. Both see the same keys and can revoke them. Name, optional expiry date and allowed IP addresses; the key (capi_…) is shown only once. Rotating issues a new one, the old one stays valid for 24 hours. The key calls exactly three endpoints: GET /forms/{id}, POST /forms/{id}/responses and POST /appointment-types/{id}/partner-links. It is valid only while the partner is active and the organization has its interface switched on; the MCP access does not accept it. The portal guide shows the ids of the forms and appointment types it may use.

2. Read the form

GET /forms/{id} lists the fields (key, required, options), the consents with id (elementId), wording (labelHtml) and whether they are required, and the pages. The agency's landing page shows the consent wording exactly as given here: only then is the consent it submits a real one. With a partner key, only forms released for the partner portal that create prospects are returned; any other answers 404. responseCount counts only the own partner's submissions, publicUrl is empty. When the organization changes the form, the definition changes with it; reading it again before a campaign is worthwhile.

curl
curl "https://www.claritos.io/api/v1/forms/<FORMULAR>?locale=de" \
  -H "Authorization: Bearer capi_…"
json
{
  "id": "<FORMULAR>",
  "fields": [
    { "key": "std:firstName", "required": true },
    { "key": "std:email", "required": true },
    { "key": "cf:interesse", "required": false, "options": [ … ] }
  ],
  "consents": [
    { "elementId": "<EINWILLIGUNG>", "required": true, "labelHtml": "<p>Ich bin einverstanden, …</p>" }
  ]
}

3. Submit a lead, booking link in the same response

POST /forms/{id}/responses with values (the keys from step 2) and consents (consent id set to true). With appointmentTypeId, an appointment type released for the partner, the personal booking link comes along; senderContactId optionally chooses on whose behalf it goes, otherwise the prospect's responsible person or the partner's owner. affiliateId is not needed: the key sets its partner itself. An Idempotency-Key per lead prevents a repeated call from creating the lead twice. If only the booking link fails, the lead stays saved: bookingUrl is then empty and bookingError names the reason (prospect-without-contact, appointment-type-not-released-to-partner, sender-not-in-team, prospect-not-found, booking-failed). If the person is already a prospect of another partner, the same rules apply as for the public form.

curl
curl -X POST https://www.claritos.io/api/v1/forms/<FORMULAR>/responses \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -H "Idempotency-Key: <je Lead eindeutig>" \
  -d '{ "values": { "std:firstName": "Anna", "std:lastName": "Muster", "std:email": "anna@example.com" },
        "consents": { "<EINWILLIGUNG>": true },
        "appointmentTypeId": "<TERMINART>" }'
json
{
  "id": "<EINSENDUNG>",
  "prospectId": "<INTERESSENT>",
  "contactId": "<KONTAKT>",
  "affiliateId": "<PARTNER>",
  "bookingUrl": "https://www.claritos.io/t/…",
  "bookingError": null
}

4. Fetch the booking link later

With the id from the response (prospectId), POST /appointment-types/{id}/partner-links returns the link again at any time, also for another released appointment type. If it already exists, the same link comes back.

curl
curl -X POST https://www.claritos.io/api/v1/appointment-types/<TERMINART>/partner-links \
  -H "Authorization: Bearer capi_…" -H "Content-Type: application/json" \
  -d '{ "prospectId": "<INTERESSENT>" }'

Errors

401: key revoked or partner no longer active; key-expired after the expiry date, also 24 hours after rotating. 403: ip-not-allowed, api-disabled (the organization's interface is off), forbidden with reason partner-key-endpoint (another endpoint). 404: form not released for partners. 422 validation-failed: at body.values.<key> or body.consents.<id> for missing required input, at body.affiliateId with code partner-affiliate when another partner is sent, at body.prospectId with code not_found for an unknown prospect or one of another partner (both look the same). 422 precondition-failed for the booking link: prospect-without-contact, appointment-type-not-released-to-partner.

With an organization key

The same endpoints also work with a key of the organization. affiliateId then chooses the partner the prospect belongs to; appointmentTypeId on submit additionally needs appointments:write, and POST /appointment-types/{id}/partner-links needs a person with the right to manage appointment scheduling. For an agency the partner key is the right way: it can do nothing else.