Skip to content
Linq

Set the OAuth integration

PUT/v1/settings/integrations/oauth

Sets your brand’s customer-authentication (OAuth) integration: the full group in one write.

Behavior

  • A present group always replaces the stored one wholly.
  • The client secret is write-only: stored by reference, never returned. Omit client_secret to keep the stored one; HTTP 400 secret_value_required when there is none to keep.
  • A write that changes token_url or issuer_url must carry client_secret: the stored secret is only ever sent to the token endpoint it was supplied for. HTTP 400 code 1061 at /client_secret when the field is omitted; the message names the endpoint that moved. authorize_url, redirect_uri, client_id and scope may change with the secret omitted.
  • An operator-attributed write (X-Cerebro-Operator honoured) may not change issuer_url, authorize_url, token_url or redirect_uri once a group is stored: HTTP 403 code 2057 with param naming the first changed field, in that order. Make the write with the brand key alone, carrying the secret. An operator’s first write, and an operator write that changes only client_id or scope, is allowed.
Body ParametersJSONExpand Collapse
authorize_url: string

The provider’s authorization endpoint.

client_id: string

The OAuth client id registered for this brand.

issuer_url: string

The provider’s issuer URL.

redirect_uri: string

The redirect URI, character-for-character as registered with the provider.

scope: string

The scope string sent on the authorize request.

token_url: string

The provider’s token endpoint.

client_secret: optional string

WRITE-ONLY. The OAuth client secret. Stored by reference in the secret store: no read ever returns it. ABSENT keeps the secret the stored group already holds; on a first write it is required (400 secret_value_required), and so is it on any write that changes token_url or issuer_url: the stored secret is never sent to a token endpoint chosen after it was supplied.

ReturnsExpand Collapse
BrandSettings object { agent_enabled, app_extensions, brand_id, 16 more }

The brand’s channel settings: what a brand edits itself, plus the managed groups and the link state, read-only.

agent_enabled: boolean

Whether your AI agent is switched on. true for a brand that has never changed it.

While this is false we stop delivering webhooks to your brand’s endpoints, so your agent is told nothing and answers nothing. Events that occur during the pause are never delivered; not held and replayed when you switch back on, which is what makes this safe to use during an incident: your agent will not wake up and answer an hour of stale messages.

The mute covers your WHOLE webhook feed, not only the events an agent answers: message.sent / message.failed, consent.expired and the invitation-batch events travel the same fan-out, so delivery outcomes and invitation status go dark too and do not backfill. The read side is unaffected and is the during-mute channel; GET /v1/messages/{msg}, GET /v1/invitations/{inv} and GET /v1/chats/{chat}/events all still answer, so nothing is lost from the record, only from the push.

It does not stop you sending. Your API key keeps working while the switch is off, which is what lets a human desk carry a conversation your agent is muted for.

app_extensions: object { apps }

The registered apps that are OPERATIVE for your brand, keyed by the same app_key space PUT /v1/settings/integrations/app_extensions writes: your stored registry when you have one, the deploy default otherwise, and managed[name=app_extensions].source says which.

Read this before assembling a write. The registry’s precedence unit is the WHOLE set: a write replaces your stored registry outright, and a stored registry masks the deploy default entirely. So an app you leave out of a write stops resolving on the send path: a 200 and a silently unregistered app. This object is what makes a correct whole-set write possible: it is the complete operative registry, so send it back with your edits applied rather than assembling one from your own records.

Unlike apple_pay and oauth above, this key is ALWAYS PRESENT: an empty apps is how “nothing is registered” is said, and it is safe to edit from. A registry is never “unconfigured” the way a merchant can be: one is always operative, so there is always an answer. The only way this key is absent is a platform build older than the field, and a client that cares about the difference should read an absent key as “cannot speak to the registry” and refuse to write the group; never as an empty one.

App icons are never returned, on the same terms as the certificate and the client secret above: app_icon_set per entry is the whole of it.

apps: unknown

The complete operative registry, app_key → app. Empty when nothing is registered. Every key here is a key PUT /v1/settings/integrations/app_extensions accepts, and that write replaces this whole map, so edit this object and send it back rather than composing one.

brand_id: string

The brand this key is scoped to: the same id GET /v1/me reports.

business_id: string

The linked business id, "" when no channel is linked. The full row is GET /v1/channels.

One of the following:
display: string

The brand’s display name.

escalation_handling: "managed" or "self_managed"

Who runs escalation for this brand: your stored choice, exactly as you set it, and self_managed until you set one. See escalation_handling on the update request for what each value means. While escalation_handling_locked is true this stored value is not operative: we have forced the brand to managed escalation.

One of the following:
"managed"
"self_managed"
escalation_handling_locked: boolean

true while we hold this brand on managed escalation regardless of its stored choice. It is the state we use when monitoring shows a partner-run human path not answering. Updates to escalation_handling are refused while it is true; contact support to release it.

managed: array of ManagedSetting { managed_by, name, set, 2 more }

The read-only groups, as display strings with a managed-by marker.

managed_by: "linq"

Who changes it. Always linq today: these are registered with the platform as well as with us, so they change together; write to your support contact.

name: "apple_pay_merchant" or "app_extensions" or "oauth"

Which setting: the Apple Pay merchant, the registered iMessage apps, or customer authentication (OAuth).

One of the following:
"apple_pay_merchant"
"app_extensions"
"oauth"
set: boolean

Whether the group is operative for a send, evaluated on the RESOLVED view: your brand’s stored configuration when present, the deploy default otherwise. false means a send needing this group is refused (409) until it is configured.

source: "brand" or "platform"

Which copy is operative: brand when your brand’s stored configuration answers, platform when the deploy default does.

One of the following:
"brand"
"platform"
value: string

A display string; merchant.com.example, 2 registered, configured, not configured. Never key material, secrets or certificate references; this field is safe to render verbatim.

policy_version: number

Monotonic policy version. Bumping it (see the update’s bump_policy_version) is what re-notifies customers of a policy change, so it only ever moves explicitly.

formatint32
privacy_policy_url: string

Absolute https URL of the privacy policy, "" when unset.

support_contact: string

The brand’s support contact, "" when unset.

terms_of_use_url: string

Absolute https URL of the terms of use, "" when unset.

apple_pay: optional object { display_name, domain, merchant_id, merchant_identity_cert_set }

The brand’s Apple Pay configuration, non-secret fields only. ABSENT when the group is not configured; never an object of empty strings, the same absent-means-unconfigured promise business_hours makes.

The merchant-identity certificate is NEVER returned, here or anywhere: it goes up write-only and lives in the secret store. merchant_identity_cert_set is the whole of what this contract says about it, so a client can offer “leave blank to keep the stored certificate” without learning anything.

display_name: string

The business name Apple renders on the payment sheet.

domain: string

The verified payment domain the merchant session is requested for.

merchant_id: string

The Apple Pay merchant identifier (merchant.example.brand).

merchant_identity_cert_set: boolean

Whether a merchant-identity certificate is on file. The certificate itself is never returned; omit merchant_identity_cert_pem on a write to keep the stored one.

business_hours: optional unknown

Business hours as stored: a LIST of ["HH:MM","HH:MM"] ranges per lowercase weekday key (monsun), so a lunch break or a split shift is expressible.

ABSENT: the key is not present at all; when the brand publishes no hours, never {} and never an invented 9-5. That is the same promise GET /v1/chats/{chat}’s brand makes about the same document: an unconfigured brand has no hours model and the platform treats it as always open, which is a different fact from “open 24/7 by policy” and is not spelled the same way.

hours_timezone: optional string

The IANA zone the hours are stated in (America/New_York). Absent when unset. Hours are never configured without it; the two keys are independently optional here only because a direct operator edit can leave a zone behind with no hours.

oauth: optional object { authorize_url, client_id, client_secret_set, 4 more }

The brand’s OAuth configuration, non-secret fields only. ABSENT when the group is not configured. The client secret is never returned, on the same terms as the certificate above.

authorize_url: string

Where the customer is sent to authorize.

client_id: string

The OAuth client id.

client_secret_set: boolean

Whether a client secret is on file. The secret itself is never returned; omit client_secret on a write to keep the stored one.

issuer_url: string

The identity provider’s issuer URL.

redirect_uri: string

The redirect the provider returns to.

scope: string

The space-separated scopes requested.

token_url: string

Where the authorization code is exchanged.

Set the OAuth integration

curl https://messages.api.linqapp.com/v1/settings/integrations/oauth \
    -X PUT \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY" \
    -d '{
          "authorize_url": "https://id.example.com/authorize",
          "client_id": "curbfare-messages",
          "issuer_url": "https://id.example.com",
          "redirect_uri": "https://auth.example.com/auth/callback",
          "scope": "openid",
          "token_url": "https://id.example.com/token"
        }'
{
  "brand_id": "biz_9f2c1a",
  "display": "CurbFare",
  "privacy_policy_url": "https://example.com/privacy",
  "terms_of_use_url": "https://example.com/terms",
  "policy_version": 3,
  "support_contact": "[email protected]",
  "richlink_default_image_att_id": "",
  "agent_enabled": true,
  "escalation_handling": "self_managed",
  "escalation_handling_locked": false,
  "consent_mode": "linq_managed",
  "business_id": "a884eddf-b0ad-4be4-9c0e-071531638768",
  "link_origin": "connect",
  "managed": [
    {
      "name": "apple_pay_merchant",
      "value": "not configured",
      "managed_by": "linq",
      "set": false,
      "source": "platform"
    },
    {
      "name": "app_extensions",
      "value": "none registered",
      "managed_by": "linq",
      "set": false,
      "source": "platform"
    },
    {
      "name": "oauth",
      "value": "configured",
      "managed_by": "linq",
      "set": true,
      "source": "brand"
    }
  ],
  "app_extensions": {
    "apps": {}
  }
}
Returns Examples
{
  "brand_id": "biz_9f2c1a",
  "display": "CurbFare",
  "privacy_policy_url": "https://example.com/privacy",
  "terms_of_use_url": "https://example.com/terms",
  "policy_version": 3,
  "support_contact": "[email protected]",
  "richlink_default_image_att_id": "",
  "agent_enabled": true,
  "escalation_handling": "self_managed",
  "escalation_handling_locked": false,
  "consent_mode": "linq_managed",
  "business_id": "a884eddf-b0ad-4be4-9c0e-071531638768",
  "link_origin": "connect",
  "managed": [
    {
      "name": "apple_pay_merchant",
      "value": "not configured",
      "managed_by": "linq",
      "set": false,
      "source": "platform"
    },
    {
      "name": "app_extensions",
      "value": "none registered",
      "managed_by": "linq",
      "set": false,
      "source": "platform"
    },
    {
      "name": "oauth",
      "value": "configured",
      "managed_by": "linq",
      "set": true,
      "source": "brand"
    }
  ],
  "app_extensions": {
    "apps": {}
  }
}