Cordial · Authentication Profile

Cordial Authentication

Authentication

Cordial secures its APIs with http and oauth2 across 2 declared security schemes, as derived from its OpenAPI definitions. OAuth 2.0 is offered via the authorizationCode flow(s).

CompanyMarketingMarketing AutomationMessagingEmailSMSPush NotificationsCustomer Data PlatformPersonalizationCustomer EngagementArtificial IntelligenceRetailE-Commerce
Methods: http, oauth2 Schemes: 2 OAuth flows: authorizationCode API key in: header

Security Schemes

basicAuth http
scheme: basic
cordialOAuth oauth2
· flows: authorizationCode

Source

Authentication Profile

Raw ↑
generated: '2026-08-04'
method: searched
source: openapi/_original/cordial-v1-openapi-original.json, openapi/_original/cordial-v2-openapi-original.json
docs: https://support.cordial.com/hc/en-us/articles/203885498-RESTful-API-summary-and-usage
note: >-
  Derived mechanically from both published Swagger documents, then upgraded from the provider's own
  authentication documentation and from the live OAuth discovery metadata served by the MCP endpoint.
  Cordial runs TWO distinct auth models on two different surfaces, which the OpenAPI alone does not
  reveal: long-lived HTTP Basic API keys on REST, and OAuth 2.1 on MCP and the CLI.

summary:
  types: [http, oauth2]
  api_key_in: [header]
  oauth2_flows: [authorizationCode]
  surfaces: 2

schemes:
- name: basicAuth
  type: http
  scheme: basic
  surface: REST API (v1 + v2)
  hosts: [api.cordial.io]
  description: HTTP Basic Authentication. Works over HTTPS
  credential: account API key
  username: the API key
  password: blank
  header_example: 'Authorization: Basic <base64 of "APIKEY:">'
  curl_example: 'curl -u "$CORDIAL_API_KEY:" https://api.cordial.io/v2/contacts'
  key_generation: 'Cordial UI: Account Settings > Account > API'
  scoped: false
  expiring: false
  rotation_api: false
  sources:
  - openapi/_original/cordial-v1-openapi-original.json
  - openapi/_original/cordial-v2-openapi-original.json
  applied_to: all 106 v2 operations and all 83 v1 operations
  failure_response:
    status: 401
    body: '{"error": "Access Denied: Authentication Failure"}'
    note: 'On 401 the `error` field is a STRING, whereas on every other error response it is a boolean.'

- name: cordialOAuth
  type: oauth2
  profile: OAuth 2.1
  surface: MCP server + CLI
  hosts: [mcp.cordial.io]
  issuer: https://mcp.cordial.io
  flows:
  - flow: authorizationCode
    authorizationUrl: https://mcp.cordial.io/oauth/authorize
    tokenUrl: https://mcp.cordial.io/oauth/token
    scopes:
      read: Read access to account data via the MCP server
      profile: Identity of the signed-in user and account
  registration_endpoint: https://mcp.cordial.io/oauth/register
  dynamic_client_registration: true
  pkce_methods: [S256, plain]
  grant_types: [authorization_code, refresh_token]
  token_endpoint_auth_methods: [client_secret_basic, client_secret_post]
  refreshable: true
  sources:
  - well-known/cordial-oauth-authorization-server.json
  - well-known/cordial-oauth-protected-resource.json
  discovery:
    oauth_authorization_server: {path: /.well-known/oauth-authorization-server, status: 200}
    oauth_protected_resource: {path: /.well-known/oauth-protected-resource, status: 200}
  challenge: >-
    An unauthenticated request returns 401 with
    WWW-Authenticate: Bearer resource_metadata="https://mcp.cordial.io/.well-known/oauth-protected-resource",
    error="invalid_token" — a correct RFC 9728 protected-resource challenge.

assessment:
  strengths:
  - 'The MCP surface is a textbook OAuth 2.1 deployment: RFC 8414 authorization-server metadata, RFC 9728 protected-resource metadata, RFC 7591 dynamic client registration, PKCE, and refresh tokens — all discoverable anonymously.'
  - 'Auth is declared and applied on 100% of operations in both Swagger documents.'
  gaps:
  - 'The REST API — the surface that can actually write data, send messages, and export contact records — is protected by a single unscoped, non-expiring account API key. There is no scope model, no key rotation endpoint, no key listing endpoint, and no per-key permission boundary in the API.'
  - 'The API key is transmitted as an HTTP Basic username, which means it lands in any tooling that logs Authorization headers or basic-auth URLs.'
  - 'No OpenID Connect, no mTLS, no signed requests, and no per-request identity: the API cannot tell which human or system made a call, only which account.'
  - 'The two auth models do not meet. An OAuth token from the MCP server cannot call REST, and a REST API key cannot call MCP. An integration spanning both must hold two unrelated credentials.'
  - 'PKCE advertises `plain` alongside `S256`. OAuth 2.1 requires S256; advertising plain is a downgrade affordance clients should not be offered.'

x-evidence:
  fetched: '2026-08-04'
  probes:
  - {url: 'https://mcp.cordial.io/.well-known/oauth-authorization-server', http_status: 200, content_type: application/json}
  - {url: 'https://mcp.cordial.io/.well-known/oauth-protected-resource', http_status: 200, content_type: application/json}
  - {url: 'https://mcp.cordial.io/mcp', method: 'POST tools/list', http_status: 401, challenge: 'Bearer invalid_token'}
  - {url: 'https://api.cordial.io/v2/', http_status: 401, body: '{"error":"Access Denied: Authentication Failure"}'}
  spec_scan: 'securityDefinitions in both documents contain exactly one scheme (basicAuth, type basic).'