Sanjay Kumar
Apr 22, 2026
  81
(1 votes)

Building Opal tools on Optimizely Connect Platform: a Mailchimp walkthrough

About the Mailchimp Opal Tool

The Mailchimp Opal Tool is an Optimizely Connect Platform app that brings Mailchimp audience operations directly into Opal workflows. It exposes developer-friendly tools for audience, contact, tag, and campaign management, with settings-driven authentication so teams can configure API access once and reuse it across automations. In addition to interactive tool calls, it includes a background sync job that can ingest Mailchimp contacts in batches and upsert them into ODP, making it useful for both real-time actions and scheduled data synchronization.

Built with TypeScript on OCP, the app follows a clear architecture: app.yml for runtime/function declarations, forms/settings.yml for secure credential inputs, Lifecycle hooks for install/settings handling, and modular @tool-decorated function classes for API operations. This structure makes the integration easy to extend, test, and maintain as Mailchimp use cases evolve.

Log in to the Optimizely Connect Platform using your OptiID credentials and install the Mailchimp Opal Tool.

OCP Developer Guide

An OCP app is app.yml + a compiled Node.js bundle + optional forms/settings + assets for directory listing content.
Opal tools are typically implemented as a single exported class extending ToolFunction, with @tool-decorated methods.

Use:

  • @zaiusinc/app-sdk for lifecycle, storage, jobs, and notifications
  • @optimizely-opal/opal-tool-ocp-sdk for tool metadata and routing shape

Who This Is For

You already know TypeScript and REST APIs. You want a checklist + mental model for OCP and a real project layout (this repo), not abstract marketing copy.

Prerequisites

  • Node.js: Align with runtime in app.yml (this project uses node22).
  • Yarn 1.x: Used in package.json scripts (npm also works if you adapt commands).
  • OCP CLI: Install via yarn global add @optimizely/ocp-cli and ensure the global bin is in PATH.
  • OCP credentials: %USERPROFILE%\.ocp\credentials.json (Windows) or ~/.ocp/credentials.json with your API key.
  • Verify CLI: ocp accounts whoami

Mental model: what runs where

Merchant installs app
    → reads app.yml (meta, runtime, functions, jobs)
    → shows forms/settings.yml
    → Lifecycle.onInstall / onSettingsForm → storage.settings.put(...)
Opal invokes a tool
    → OCP loads entry_point class (e.g. MailchimpOpalToolFunction)
    → method decorated with @tool(...) executes with authData.credentials
Job scheduler runs sync_contacts
    → Job.prepare / Job.perform loop until status.complete

Minimal project skeleton
Suggested layout (matches this repo):

mailchimp-opal-tool/
├── app.yml                 # OCP manifest
├── package.json
├── tsconfig.json          # decorators: true (required for @tool)
├── forms/
│   └── settings.yml     # merchant-facing settings schema
├── assets/
│   └── directory/          # marketplace copy, icons, etc.
└── src/
    ├── index.ts             # export entry_point classes + Lifecycle
    ├── api-client.ts
    ├── types.ts
    ├── lifecycle/
    │   └── Lifecycle.ts
    ├── functions/       # ToolFunction subclasses + @tool methods
    ├── jobs/               # optional Job subclasses
    ├── odp/                # mappers (e.g. Mailchimp → ODP)
    └── data/               # types for API responses / job state

Dependencies you actually care about

From package.json:

@optimizely-opal/opal-tool-ocp-sdk ToolFunction, @tool, ParameterType.
@zaiusinc/app-sdk Lifecycle, Job, storage, logger, notifications, Request.
@zaiusinc/node-sdk  ODP writes (e.g. z.customer(batch) in jobs).
axios  HTTP client to the third-party API (here, Mailchimp).

app.yml: the contract with OCP

Important fields in this app:

  • meta.app_id: stable identifier (here: mailchimpopaltool).
  • runtime: must match your local/toolchain (here: node22).
  • functions.opal_tool:
    • opal_tool: true
    • entry_point: must match an exported class name from your bundle (here: MailchimpOpalToolFunction).
  • jobs.sync_contacts:  
    • entry_point: SyncContacts  exported Job subclass.

If entry_point does not match an export, validation or deploy will fail in confusing ways  treat exports as part of your public API.

src/index.ts: exports are the wiring

Pattern used here:

  • Export the Opal tool class the manifest names.
  • Export Lifecycle for install/settings/upgrade hooks.

The Opal tool class in this project is a re-export alias: MailchimpOpalToolFunction points at the leaf subclass that includes all tools (audience → tag → contact → campaign stack), so app.yml stays stable while you add layers in functions/.

Opal tools: implementation recipe

  1. Extend ToolFunction from @optimizely-opal/opal-tool-ocp-sdk.
  2. On each public tool method, add @tool({ name, description, endpoint, parameters }).
  3. Use ParameterType.* and required: true | false so Opal gets a proper JSON schema for the agent.
  4. Resolve HTTP client from authData.credentials (throw a clear error if missing).
  5. Return a predictable JSON object (this codebase uses { success, data } / { success: false, error }) so agents can branch without parsing stack traces.

Mailchimp-specific tip: member endpoints use the MD5 hash of lowercased email as the path segment; this repo centralizes that in a base class helper.

Jobs: SyncContacts pattern

Use a job when:

  • Work exceeds a single request/response cycle.
  • You need paginationretries, and notifications on completion/failure.

Implementation notes from this job:

  • prepare:  initialize status.state (offset, count, retries, list id).
  • perform: fetch page → map to ODP payloads → batch upsert (chunk size tuned to ODP limits) → advance offset → set status.complete when done.
  • Retries:  bounded retries + backoff; on exhaustion, notify and complete. 

Build

yarn install
yarn build
ocp app validate
ocp dev

CLI quick reference

ocp accounts whoami
ocp app validate
yarn build
ocp dev
ocp app prepare
ocp directory publish
ocp app logs --appId <YOUR_APP_ID>


More step-by-step PowerShell setup lives in script.

Debugging checklist

  • ocp app validate fails,  Check entry_point strings vs src/index.ts exports; check runtime vs local Node.
  • Tools fail at runtime with “credentials required”, Settings section key mismatch: ensure onSettingsForm saved the section your tool reads (credentials in this app).
  • Decorators seem ignored tsconfig / wrong outDir; ensure you are running the built output OCP expects.

Job stuck or repeating Inspect status.state logging; ensure status.complete is set on all exit paths; avoid throwing without updating state if you intend to retry.

Extending this app safely

  • New tool: add a method + @tool on the appropriate class in the inheritance chain, or add a new subclass and change the final export if you want a cleaner split.
  • New setting: add field to forms/settings.yml, validate in Lifecycle, read in tools/jobs.
  • New job: add class under src/jobs/, export if needed, add block under jobs: in app.yml.

Keep manifest, exports, and form keys in sync, that is the most common source of integration bugs.


Thanks for visting!

Apr 22, 2026

Comments

Please login to comment.
Latest blogs
📣 Build, Automate, and Scale Content Operations with CMS REST API v1 - now live!

Now available for both CMS13 and CMS SaaS We are excited to announce the  v1 release of our CMS REST API —a major milestone in delivering a stable,...

Kathy Copeland | Apr 21, 2026

Introducing AI Assistant v4 for Optimizely CMS 12 and 13

Epicweb AI Assistant v4.0 adds full support for Optimizely CMS 13 on .NET 10 while staying compatible with CMS 12 on .NET 8, plus new AI Chat tools...

Luc Gosso (MVP) | Apr 20, 2026 |

Remote Debugging in Optimizely DXP: What Is Actually Possible?

Introduction At SYZYGY Techsolutions , we support Optimizely DXP projects at scale, so continuously  identifying  the right tools and approaches fo...

Mike | Apr 20, 2026