🎁 Get the FREE AI Skills Starter GuideSubscribe →
BytesAgainBytesAgain
🦀 ClawHub

Constant Contact

by @byungkyu

Constant Contact API integration with managed OAuth. This is a write-capable integration — it can read, create, update, delete, and bulk-modify contacts, ema...

Versionv1.0.5
Downloads6,150
Stars3
TERMINAL
clawhub install constant-contact

📖 About This Skill


name: constant-contact description: | Constant Contact API integration with managed OAuth. This is a write-capable integration — it can read, create, update, delete, and bulk-modify contacts, email campaigns, contact lists, tags, custom fields, segments, and marketing analytics. Use this skill when users want to interact with Constant Contact marketing data. All write operations (POST, PUT, DELETE, bulk actions, campaign sending/scheduling) require explicit user approval with specific resource identifiers before execution. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key. metadata: author: maton version: "1.0" clawdbot: emoji: 🧠 requires: env: - MATON_API_KEY

Constant Contact

Access the Constant Contact V3 API with managed OAuth authentication. Manage contacts, email campaigns, contact lists, tags, custom fields, segments, bulk operations, and marketing analytics.

Quick Start

# List contacts
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/constant-contact/v3/contacts')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Base URL

https://api.maton.ai/constant-contact/v3/{resource}

Maton proxies requests to api.cc.email/v3 and automatically injects your OAuth token.

Authentication

All requests require the Maton API key in the Authorization header:

Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. Copy your API key

Connection Management

Manage your Constant Contact OAuth connections at https://api.maton.ai.

List Connections

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=constant-contact&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Create Connection

python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'constant-contact'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Get Connection

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Response:

{
  "connection": {
    "connection_id": "{connection_id}",
    "status": "ACTIVE",
    "creation_time": "2026-02-07T07:41:05.859244Z",
    "last_updated_time": "2026-02-07T07:41:32.658230Z",
    "url": "https://connect.maton.ai/?session_token=...",
    "app": "constant-contact",
    "metadata": {}
  }
}

Open the returned url in a browser to complete OAuth authorization.

Delete Connection

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Specifying Connection

If you have multiple Constant Contact connections, specify which one to use with the Maton-Connection header:

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/constant-contact/v3/contacts')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Always include the Maton-Connection header to ensure requests go to the intended account, especially before any write operation. If you have multiple connections and omit this header, the gateway uses the default connection, which may not be the intended account.

Security & Permissions

  • Access is scoped to contacts, email campaigns, lists, segments, tags, custom fields, and marketing analytics within the connected Constant Contact account. Only install if you need Constant Contact administration. Revoke unused connections promptly.
  • Default to read-only operations. Always start by listing or retrieving resources to confirm identifiers before proposing any changes.
  • All write operations require explicit user approval with specific identifiers. Before executing any POST, PUT, PATCH, or DELETE call:
  • 1. Retrieve and display the target resource (contact email, list name, campaign name/ID) so the user can verify. 2. Clearly describe the intended effect (e.g., "This will delete contact 'john@example.com' (ID: abc123) from your account"). 3. Wait for explicit user confirmation before proceeding.
  • High-impact operations require extra caution. Sending/scheduling email campaigns, bulk contact deletions, bulk list membership changes, and importing contacts can affect large numbers of marketing contacts and external recipients. These actions must include a summary of consequences and require confirmation.
  • Campaign sending is irreversible — emails are delivered to external recipients immediately. Always preview the campaign and confirm recipients, subject, and content before sending or scheduling.
  • API Reference

    Account

    #### Get Account Summary

    GET /constant-contact/v3/account/summary
    

    Response:

    {
      "contact_email": "user@example.com",
      "contact_phone": "5551234567",
      "country_code": "us",
      "encoded_account_id": "abc123",
      "first_name": "John",
      "last_name": "Doe",
      "organization_name": "Acme Inc",
      "state_code": "CA",
      "time_zone_id": "US/Eastern"
    }
    

    #### Update Account Summary

    PUT /constant-contact/v3/account/summary
    Content-Type: application/json

    { "first_name": "John", "last_name": "Doe", "organization_name": "Acme Inc", "time_zone_id": "US/Eastern" }

    #### Get Account Emails

    Returns confirmed sender email addresses for the account.

    GET /constant-contact/v3/account/emails
    

    Response:

    [
      {
        "email_id": 1,
        "email_address": "marketing@example.com",
        "roles": ["BILLING", "CONTACT", "DEFAULT_FROM", "REPLY_TO"],
        "confirm_status": "CONFIRMED",
        "confirm_time": "2026-02-05T07:32:49.766+0000",
        "confirm_source_type": "SITE_OWNER"
      }
    ]
    

    #### Add Account Email

    POST /constant-contact/v3/account/emails
    Content-Type: application/json

    { "email_address": "newsender@example.com" }

    A confirmation email will be sent to the address. The email must be confirmed before it can be used as a sender.

    #### Get User Privileges

    GET /constant-contact/v3/account/user/privileges
    

    Contacts

    #### List Contacts

    GET /constant-contact/v3/contacts
    

    Query parameters:

  • status - Filter by status: all, active, deleted, not_set, pending_confirmation, temp_hold, unsubscribed
  • email - Filter by exact email address
  • lists - Filter by list ID(s), comma-separated
  • segment_id - Filter by segment ID
  • tags - Filter by tag ID(s), comma-separated
  • updated_after - ISO-8601 date filter (e.g., 2026-04-01T00:00:00Z)
  • include - Include subresources: custom_fields, list_memberships, taggings, notes (comma-separated)
  • limit - Results per page (default 50, max 500)
  • Example with filters:

    GET /constant-contact/v3/contacts?email=john@example.com&status=all
    GET /constant-contact/v3/contacts?updated_after=2026-04-01T00:00:00Z&limit=100
    GET /constant-contact/v3/contacts?include=custom_fields,list_memberships,taggings&limit=50
    

    #### Get Contact

    GET /constant-contact/v3/contacts/{contact_id}
    

    Query parameters:

  • include - Include subresources: custom_fields, list_memberships, taggings, notes (comma-separated)
  • Example:

    GET /constant-contact/v3/contacts/{contact_id}?include=custom_fields,list_memberships,taggings,notes
    

    Response:

    {
      "contact_id": "uuid",
      "email_address": {
        "address": "john@example.com",
        "permission_to_send": "implicit",
        "created_at": "2026-04-28T21:46:22Z",
        "updated_at": "2026-04-28T21:46:22Z",
        "opt_in_source": "Account",
        "opt_in_date": "2026-04-28T21:46:22Z",
        "confirm_status": "off"
      },
      "first_name": "John",
      "last_name": "Doe",
      "create_source": "Account",
      "created_at": "2026-04-28T21:46:22Z",
      "updated_at": "2026-04-28T21:46:22Z",
      "custom_fields": [],
      "list_memberships": ["list-uuid"],
      "taggings": [],
      "notes": []
    }
    

    #### Create Contact

    IMPORTANT: The create_source field is required.

    POST /constant-contact/v3/contacts
    Content-Type: application/json

    { "email_address": { "address": "john@example.com", "permission_to_send": "implicit" }, "first_name": "John", "last_name": "Doe", "job_title": "Developer", "company_name": "Acme Inc", "create_source": "Account", "list_memberships": ["list-uuid-here"] }

    Valid create_source values: Account, Contact, Landing Page

    #### Update Contact

    IMPORTANT: The update_source field is required.

    PUT /constant-contact/v3/contacts/{contact_id}
    Content-Type: application/json

    { "email_address": { "address": "john@example.com" }, "first_name": "John", "last_name": "Smith", "update_source": "Account" }

    Valid update_source values: Account, Contact, Landing Page

    #### Delete Contact

    DELETE /constant-contact/v3/contacts/{contact_id}
    

    Returns 204 No Content on success.

    #### Create or Update Contact (Sign-Up Form)

    Use this endpoint to create a new contact or update an existing one by email address without checking if they exist first:

    POST /constant-contact/v3/contacts/sign_up_form
    Content-Type: application/json

    { "email_address": "john@example.com", "first_name": "John", "last_name": "Doe", "list_memberships": ["list-uuid-here"] }

    Response:

    {
      "contact_id": "uuid",
      "action": "created"
    }
    

    The action field indicates whether the contact was created or updated.

    #### Get Contact Counts

    GET /constant-contact/v3/contacts/counts
    

    Response:

    {
      "total": 150,
      "explicit": 100,
      "implicit": 40,
      "pending": 5,
      "unsubscribed": 5
    }
    

    Contact Lists

    #### List Contact Lists

    GET /constant-contact/v3/contact_lists
    

    Query parameters:

  • include_count - Include total list count (true/false)
  • include_membership_count - Include contact count per list: all, active, unsubscribed
  • limit - Results per page
  • Example:

    GET /constant-contact/v3/contact_lists?include_membership_count=all
    

    Response:

    {
      "lists": [
        {
          "list_id": "uuid",
          "name": "Newsletter Subscribers",
          "description": "Main newsletter",
          "favorite": false,
          "created_at": "2026-02-05T07:19:59Z",
          "updated_at": "2026-02-05T07:19:59Z",
          "membership_count": 150
        }
      ],
      "lists_count": 1
    }
    

    #### Get Contact List

    GET /constant-contact/v3/contact_lists/{list_id}
    

    Query parameters:

  • include_membership_count - Include membership count: all, active, unsubscribed
  • #### Create Contact List

    POST /constant-contact/v3/contact_lists
    Content-Type: application/json

    { "name": "Newsletter Subscribers", "description": "Main newsletter list", "favorite": false }

    #### Update Contact List

    PUT /constant-contact/v3/contact_lists/{list_id}
    Content-Type: application/json

    { "name": "Updated List Name", "description": "Updated description", "favorite": true }

    #### Delete Contact List

    DELETE /constant-contact/v3/contact_lists/{list_id}
    

    Returns 202 Accepted (deletion is asynchronous).

    Tags

    #### List Tags

    GET /constant-contact/v3/contact_tags
    

    Query parameters:

  • limit - Results per page
  • #### Create Tag

    POST /constant-contact/v3/contact_tags
    Content-Type: application/json

    { "name": "VIP Customer" }

    #### Update Tag

    PUT /constant-contact/v3/contact_tags/{tag_id}
    Content-Type: application/json

    { "name": "Premium Customer" }

    #### Delete Tag

    DELETE /constant-contact/v3/contact_tags/{tag_id}
    

    Returns 202 Accepted (deletion is asynchronous).

    Custom Fields

    #### List Custom Fields

    GET /constant-contact/v3/contact_custom_fields
    

    #### Create Custom Field

    POST /constant-contact/v3/contact_custom_fields
    Content-Type: application/json

    { "label": "Customer ID", "type": "string" }

    Valid types: string, date

    Response:

    {
      "custom_field_id": "uuid",
      "label": "Customer ID",
      "name": "customer_id",
      "type": "string",
      "version": 1,
      "created_at": "2026-04-28T21:45:57Z",
      "updated_at": "2026-04-28T21:45:57Z"
    }
    

    #### Delete Custom Field

    DELETE /constant-contact/v3/contact_custom_fields/{custom_field_id}
    

    Email Campaigns

    #### List Email Campaigns

    GET /constant-contact/v3/emails
    

    Query parameters:

  • limit - Results per page (default 50)
  • before_date - ISO-8601 date filter
  • after_date - ISO-8601 date filter
  • Response:

    {
      "campaigns": [
        {
          "campaign_id": "uuid",
          "name": "March Newsletter",
          "current_status": "Draft",
          "type": "CUSTOM_CODE_EMAIL",
          "type_code": 26,
          "created_at": "2026-04-28T21:47:35.000Z",
          "updated_at": "2026-04-28T21:47:35.000Z"
        }
      ]
    }
    

    #### Get Email Campaign

    GET /constant-contact/v3/emails/{campaign_id}
    

    Response includes campaign activity IDs:

    {
      "campaign_activities": [
        {
          "campaign_activity_id": "uuid",
          "role": "primary_email"
        },
        {
          "campaign_activity_id": "uuid",
          "role": "permalink"
        }
      ],
      "campaign_id": "uuid",
      "current_status": "DRAFT",
      "name": "March Newsletter",
      "type": "CUSTOM_CODE_EMAIL"
    }
    

    #### Create Email Campaign

    POST /constant-contact/v3/emails
    Content-Type: application/json

    { "name": "March Newsletter", "email_campaign_activities": [ { "format_type": 5, "from_name": "Company Name", "from_email": "marketing@example.com", "reply_to_email": "reply@example.com", "subject": "March Newsletter", "html_content": "

    Hello!

    " } ] }

    The from_email must be a confirmed account email address (see Account Emails).

    #### Rename Email Campaign

    PATCH /constant-contact/v3/emails/{campaign_id}
    Content-Type: application/json

    { "name": "New Campaign Name" }

    #### Delete Email Campaign

    DELETE /constant-contact/v3/emails/{campaign_id}
    

    Returns 204 No Content on success.

    Email Campaign Activities

    Campaign activities are the content/configuration of a campaign. Use the campaign_activity_id with role primary_email from the campaign response.

    #### Get Campaign Activity

    GET /constant-contact/v3/emails/activities/{campaign_activity_id}
    

    Response:

    {
      "campaign_activity_id": "uuid",
      "campaign_id": "uuid",
      "role": "primary_email",
      "contact_list_ids": [],
      "segment_ids": [],
      "current_status": "DRAFT",
      "format_type": 5,
      "from_email": "marketing@example.com",
      "from_name": "Company",
      "reply_to_email": "reply@example.com",
      "subject": "Newsletter"
    }
    

    #### Update Campaign Activity

    Updates the email content, targeting, and sender information. All fields in the request body are replaced.

    PUT /constant-contact/v3/emails/activities/{campaign_activity_id}
    Content-Type: application/json

    { "from_name": "Updated Name", "from_email": "marketing@example.com", "reply_to_email": "reply@example.com", "subject": "Updated Subject", "html_content": "

    Updated Content

    ", "contact_list_ids": ["list-uuid-here"] }

    IMPORTANT: from_email is required in the update body. Omitting it returns a validation error.

    #### Preview Campaign Activity

    Returns the rendered HTML and text preview of the email.

    GET /constant-contact/v3/emails/activities/{campaign_activity_id}/previews
    

    Response:

    {
      "campaign_activity_id": "uuid",
      "from_email": "marketing@example.com",
      "from_name": "Company",
      "preview_html_content": "...",
      "preview_text_content": "Plain text version...",
      "reply_to_email": "reply@example.com",
      "subject": "Newsletter"
    }
    

    #### Send Test Email

    Sends a test/proof version of the email to specified addresses.

    POST /constant-contact/v3/emails/activities/{campaign_activity_id}/tests
    Content-Type: application/json

    { "email_addresses": ["test@example.com"], "personal_message": "Please review this draft" }

    Returns 204 No Content on success.

    #### Schedule Email Campaign

    POST /constant-contact/v3/emails/activities/{campaign_activity_id}/schedules
    Content-Type: application/json

    { "scheduled_date": "2026-06-01T10:00:00Z" }

    Note: The campaign activity must have a valid from_email, a physical address on the account, and at least one target list or segment before scheduling.

    #### Get Campaign Schedule

    GET /constant-contact/v3/emails/activities/{campaign_activity_id}/schedules
    

    #### Unschedule Email Campaign

    DELETE /constant-contact/v3/emails/activities/{campaign_activity_id}/schedules
    

    #### Get Non-Opener Resend

    GET /constant-contact/v3/emails/activities/{campaign_activity_id}/non_opener_resends
    

    Returns resend details for sent campaigns. Returns empty array if no resend is configured.

    #### Get A/B Test

    GET /constant-contact/v3/emails/activities/{campaign_activity_id}/abtest
    

    Segments

    #### List Segments

    GET /constant-contact/v3/segments
    

    Query parameters:

  • sort_by - Sort field (e.g., name, date)
  • sort_order - asc or desc
  • #### Get Segment

    GET /constant-contact/v3/segments/{segment_id}
    

    #### Create Segment

    Segments use a criteria object to define the audience filter:

    POST /constant-contact/v3/segments
    Content-Type: application/json

    { "name": "Engaged Subscribers", "segment_criteria": { "version": "3.0.0", "criteria": { ... } } }

    Note: The segment_criteria must be a JSON object (not a string). The criteria schema is complex and version-dependent. Refer to the Constant Contact Segments Documentation for the full criteria format.

    #### Update Segment

    PUT /constant-contact/v3/segments/{segment_id}
    Content-Type: application/json

    { "name": "Updated Segment Name", "segment_criteria": { ... } }

    #### Delete Segment

    DELETE /constant-contact/v3/segments/{segment_id}
    

    Bulk Activities

    Bulk activities run asynchronously. After creating a bulk activity, poll the activity status endpoint until completion.

    #### List Activities

    GET /constant-contact/v3/activities
    

    Query parameters:

  • limit - Results per page
  • state - Filter by state: processing, completed, cancelled, failed, timed_out
  • #### Get Activity Status

    GET /constant-contact/v3/activities/{activity_id}
    

    Response:

    {
      "activity_id": "uuid",
      "state": "completed",
      "started_at": "2026-04-28T21:48:16Z",
      "completed_at": "2026-04-28T21:48:16Z",
      "created_at": "2026-04-28T21:48:15Z",
      "updated_at": "2026-04-28T21:48:16Z",
      "percent_done": 100,
      "activity_errors": [],
      "status": {
        "items_total_count": 1,
        "items_completed_count": 1
      }
    }
    

    #### Add Contacts to Lists

    POST /constant-contact/v3/activities/add_list_memberships
    Content-Type: application/json

    { "source": { "contact_ids": ["contact-uuid-1", "contact-uuid-2"] }, "list_ids": ["list-uuid"] }

    The source can also use list_ids to copy contacts from other lists:

    {
      "source": {
        "list_ids": ["source-list-uuid"]
      },
      "list_ids": ["target-list-uuid"]
    }
    

    #### Remove Contacts from Lists

    POST /constant-contact/v3/activities/remove_list_memberships
    Content-Type: application/json

    { "source": { "contact_ids": ["contact-uuid-1", "contact-uuid-2"] }, "list_ids": ["target-list-uuid"] }

    #### Add Tags to Contacts

    POST /constant-contact/v3/activities/contacts_taggings_add
    Content-Type: application/json

    { "source": { "contact_ids": ["contact-uuid-1", "contact-uuid-2"] }, "tag_ids": ["tag-uuid"] }

    #### Remove Tags from Contacts

    POST /constant-contact/v3/activities/contacts_taggings_remove
    Content-Type: application/json

    { "source": { "contact_ids": ["contact-uuid-1", "contact-uuid-2"] }, "tag_ids": ["tag-uuid"] }

    #### Export Contacts

    POST /constant-contact/v3/activities/contact_exports
    Content-Type: application/json

    { "contact_ids": ["contact-uuid-1", "contact-uuid-2"], "fields": ["first_name", "last_name", "email"] }

    The response includes a results link to download the export:

    {
      "activity_id": "uuid",
      "state": "initialized",
      "_links": {
        "self": { "href": "/v3/activities/{activity_id}" },
        "results": { "href": "/v3/contact_exports/{export_id}" }
      }
    }
    

    #### Download Contact Export

    After the export activity completes, download the CSV:

    GET /constant-contact/v3/contact_exports/{export_id}
    

    Returns CSV data.

    #### Import Contacts

    POST /constant-contact/v3/activities/contacts_file_import
    Content-Type: multipart/form-data

    {file: contacts.csv, list_ids: ["list-uuid"]}

    #### Delete Contacts in Bulk

    POST /constant-contact/v3/activities/contact_delete
    Content-Type: application/json

    { "contact_ids": ["contact-uuid-1", "contact-uuid-2"] }

    Reporting

    #### Email Campaign Summaries

    GET /constant-contact/v3/reports/summary_reports/email_campaign_summaries
    

    Response:

    {
      "bulk_email_campaign_summaries": [...],
      "aggregate_percents": {
        "click": 5.2,
        "open": 22.1,
        "did_not_open": 72.7,
        "bounce": 1.3,
        "unsubscribe": 0.2
      }
    }
    

    #### Get Email Campaign Report

    Returns detailed metrics for a specific sent campaign activity.

    GET /constant-contact/v3/reports/email_reports/{campaign_activity_id}
    

    Note: Only available for sent campaigns. Draft campaigns return 404.

    #### Contact Activity Summary

    GET /constant-contact/v3/reports/contact_reports/{contact_id}/activity_summary
    

    Response:

    {
      "contact_id": "uuid",
      "campaign_activities": [
        {
          "campaign_activity_id": "uuid",
          "sends": 1,
          "opens": 1,
          "clicks": 0,
          "bounces": 0
        }
      ]
    }
    

    Pagination

    The API uses cursor-based pagination with a limit parameter:

    GET /constant-contact/v3/contacts?limit=50
    

    Response includes pagination links:

    {
      "contacts": [...],
      "_links": {
        "next": {
          "href": "/v3/contacts?cursor=abc123"
        }
      }
    }
    

    Use the cursor from the next link for subsequent pages:

    GET /constant-contact/v3/contacts?cursor=abc123
    

    When there are no more pages, the _links.next field is absent from the response.

    Code Examples

    JavaScript

    const response = await fetch(
      'https://api.maton.ai/constant-contact/v3/contacts?limit=50',
      {
        headers: {
          'Authorization': Bearer ${process.env.MATON_API_KEY}
        }
      }
    );
    const data = await response.json();
    

    Python

    import os
    import requests

    response = requests.get( 'https://api.maton.ai/constant-contact/v3/contacts', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}, params={'limit': 50} ) data = response.json()

    Notes

  • Resource IDs use UUID format (36 characters with hyphens)
  • All dates use ISO-8601 format: YYYY-MM-DDThh:mm:ss.sZ
  • Maximum 1,000 contact lists per account
  • A contact can belong to up to 50 lists
  • Bulk operations are asynchronous - check activity status for completion
  • Email campaigns require confirmed sender email addresses
  • format_type: 5 for custom HTML emails
  • create_source is required when creating contacts; update_source is required when updating
  • Scheduling a campaign requires a valid physical address on the account and at least one target list
  • Delete operations on tags and lists return 202 Accepted (asynchronous); contacts and campaigns return 204 No Content
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments
  • Error Handling

    | Status | Meaning | |--------|---------| | 400 | Missing Constant Contact connection or invalid request | | 401 | Invalid or missing Maton API key, or OAuth token expired | | 403 | Insufficient permissions for the requested operation | | 404 | Resource not found | | 409 | Conflict (e.g., duplicate email address) | | 429 | Rate limited | | 4xx/5xx | Passthrough error from Constant Contact API |

    Error Response Format

    [
      {
        "error_key": "contacts.api.validation.error",
        "error_message": "create_source is missing, create_source does not have a valid value"
      }
    ]
    

    Troubleshooting: API Key Issues

    1. Check that the MATON_API_KEY environment variable is set:

    echo $MATON_API_KEY
    

    2. Verify the API key is valid by listing connections:

    python <<'EOF'
    import urllib.request, os, json
    req = urllib.request.Request('https://api.maton.ai/connections')
    req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
    print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
    EOF
    

    Troubleshooting: Invalid App Name

    1. Ensure your URL path starts with constant-contact. For example:

  • Correct: https://api.maton.ai/constant-contact/v3/contacts
  • Incorrect: https://api.maton.ai/v3/contacts
  • Resources

  • Constant Contact V3 API Overview
  • API Reference
  • Technical Overview
  • Contacts Overview
  • Email Campaigns Guide
  • Contact Lists Overview
  • Maton Community
  • Maton Support
  • 💡 Examples

    # List contacts
    python <<'EOF'
    import urllib.request, os, json
    req = urllib.request.Request('https://api.maton.ai/constant-contact/v3/contacts')
    req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
    print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
    EOF
    

    📋 Tips & Best Practices

  • Resource IDs use UUID format (36 characters with hyphens)
  • All dates use ISO-8601 format: YYYY-MM-DDThh:mm:ss.sZ
  • Maximum 1,000 contact lists per account
  • A contact can belong to up to 50 lists
  • Bulk operations are asynchronous - check activity status for completion
  • Email campaigns require confirmed sender email addresses
  • format_type: 5 for custom HTML emails
  • create_source is required when creating contacts; update_source is required when updating
  • Scheduling a campaign requires a valid physical address on the account and at least one target list
  • Delete operations on tags and lists return 202 Accepted (asynchronous); contacts and campaigns return 204 No Content
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments