Skip to content
Skillv1.0.0

clari-sdk-patterns

Production-ready Clari API client patterns in Python and TypeScript. Use when building reusable Clari clients, implementing export pipelines, or wrapping the Clari v4 API for team use. Trigger with ph

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (plugins/saas-packs/clari-pack/skills/clari-sdk-patterns/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill clari-sdk-patterns. Copyright stays with the author (MIT).

Clari SDK Patterns

Overview

Clari has no official SDK -- build typed wrappers around the v4 REST API. These patterns cover the Export API for forecasts, job polling, and data transformation pipelines.

Prerequisites

  • Completed clari-install-auth setup
  • Python 3.10+ (primary) or TypeScript 5+

Instructions

Step 1: Python Client

# clari_client.py
import os
import time
import requests
from dataclasses import dataclass, field
from typing import Optional

@dataclass
class ClariConfig:
    api_key: str
    base_url: str = "https://api.clari.com/v4"
    poll_interval: int = 5
    max_poll_attempts: int = 60

class ClariClient:
    def __init__(self, config: Optional[ClariConfig] = None):
        self.config = config or ClariConfig(
            api_key=os.environ["CLARI_API_KEY"]
        )
        self.session = requests.Session()
        self.session.headers.update({
            "apikey": self.config.api_key,
            "Content-Type": "text/plain",
        })

    def list_forecasts(self) -> list[dict]:
        resp = self.session.get(f"{self.config.base_url}/export/forecast/list")
        resp.raise_for_status()
        return resp.json()["forecasts"]

    def export_forecast(
        self,
        forecast_name: str,
        time_period: str,
        types: list[str] = None,
        currency: str = "USD",
        export_format: str = "JSON",
    ) -> dict:
        payload = {
            "timePeriod": time_period,
            "typesToExport": types or [
                "forecast", "quota", "forecast_updated",
                "adjustment", "crm_total", "crm_closed"
            ],
            "currency": currency,
            "schedule": "NONE",
            "includeHistorical": False,
            "exportFormat": export_format,
        }

        resp = self.session.post(
            f"{self.config.base_url}/export/forecast/{forecast_name}",
            json=payload,
        )
        resp.raise_for_status()
        return resp.json()

    def wait_for_job(self, job_id: str) -> dict:
        for attempt in range(self.config.max_poll_attempts):
            resp = self.session.get(
                f"{self.config.base_url}/export/jobs/{job_id}",
            )
            resp.raise_for_status()
            status = resp.json()

            if status["status"] == "COMPLETED":
                return status
            if status["status"] == "FAILED":
                raise ClariExportError(f"Job {job_id} failed: {status}")

            time.sleep(self.config.poll_interval)

        raise ClariExportError(f"Job {job_id} timed out after {self.config.max_poll_attempts} attempts")

    def download_export(self, download_url: str) -> dict:
        resp = requests.get(download_url)
        resp.raise_for_status()
        return resp.json()

    def export_and_download(
        self, forecast_name: str, time_period: str
    ) -> dict:
        job = self.export_forecast(forecast_name, time_period)
        completed = self.wait_for_job(job["jobId"])
        return self.download_export(completed["downloadUrl"])

class ClariExportError(Exception):
    pass

Step 2: TypeScript Client

// clari-client.ts
interface ClariConfig {
  apiKey: string;
  baseUrl?: string;
  pollIntervalMs?: number;
  maxPollAttempts?: number;
}

interface ForecastExport {
  entries: ForecastEntry[];
}

interface ForecastEntry {
  ownerName: string;
  ownerEmail: string;
  forecastAmount: number;
  quotaAmount: number;
  crmTotal: number;
  crmClosed: number;
  adjustmentAmount: number;
  timePeriod: string;
}

class ClariClient {
  private apiKey: string;
  private baseUrl: string;
  private pollIntervalMs: number;
  private maxPollAttempts: number;

  constructor(config: ClariConfig) {
    this.apiKey = config.apiKey;
    this.baseUrl = config.baseUrl ?? "https://api.clari.com/v4";
    this.pollIntervalMs = config.pollIntervalMs ?? 5000;
    this.maxPollAttempts = config.maxPollAttempts ?? 60;
  }

  private async request<T>(path: string, options?: RequestInit): Promise<T> {
    const response = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: {
        apikey: this.apiKey,
        "Content-Type": "text/plain",
        ...options?.headers,
      },
    });

    if (!response.ok) {
      throw new Error(`Clari API ${response.status}: ${await response.text()}`);
    }

    return response.json();
  }

  async listForecasts(): Promise<{ forecasts: any[] }> {
    return this.request("/export/forecast/list");
  }

  async exportForecast(forecastName: string, timePeriod: string): Promise<any> {
    return this.request(`/export/forecast/${forecastName}`, {
      method: "POST",
      body: JSON.stringify({
        timePeriod,
        typesToExport: ["forecast", "quota", "crm_total", "crm_closed"],
        currency: "USD",
        schedule: "NONE",
        includeHistorical: false,
        exportFormat: "JSON",
      }),
    });
  }

  async exportAndDownload(
    forecastName: string,
    timePeriod: string
  ): Promise<ForecastExport> {
    const job = await this.exportForecast(forecastName, timePeriod);
    const completed = await this.waitForJob(job.jobId);
    const resp = await fetch(completed.downloadUrl);
    return resp.json();
  }

  private async waitForJob(jobId: string): Promise<any> {
    for (let i = 0; i < this.maxPollAttempts; i++) {
      const status = await this.request(`/export/jobs/${jobId}`);
      if (status.status === "COMPLETED") return status;
      if (status.status === "FAILED") throw new Error(`Job failed: ${jobId}`);
      await new Promise((r) => setTimeout(r, this.pollIntervalMs));
    }
    throw new Error(`Job ${jobId} timed out`);
  }
}

Error Handling

Status Meaning Action
401 Invalid API key Regenerate token
403 Insufficient permissions Admin must grant API access
404 Wrong forecast name List forecasts first
429 Rate limited Back off and retry

Output

The client returns validated forecast/export results or a classified failure with a job ID and safe correlation metadata. It never logs bearer credentials, download URLs, or unfiltered forecast payloads; callers receive a bounded retry decision rather than an unstructured provider exception.

Examples

Submit a read-only export for a staging period, poll the returned job ID within the configured attempt limit, and persist only the approved fields after a completed response. On 429, honor the backoff policy; on 401 or 403, stop and route the request to the credential or authorization owner instead of retrying.

Resources

Next Steps

Apply patterns in clari-core-workflow-a for forecast export pipelines.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-clari-sdk-patterns/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-clari-sdk-patterns.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-clari-sdk-patterns",
  "kind": "skill",
  "name": "clari-sdk-patterns",
  "description": "Production-ready Clari API client patterns in Python and TypeScript. Use when building reusable Clari clients, implementing export pipelines, or wrapping the Clari v4 API for team use. Trigger with phrases like \"clari API patterns\", \"clari client wrapper\", \"clari Python client\", \"clari TypeScript client\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "saas",
      "revenue-intelligence",
      "forecasting",
      "clari",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Production-ready Clari API client patterns in Python and TypeScript. Use when building reusable Clari clients, implementing export pipelines, or wrapping the Clari v4 API for team use. Trigger with phrases like \"clari API patterns\", \"clari client wrapper\", \"clari Python client\", \"clari TypeScript client\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "plugins/saas-packs/clari-pack/skills/clari-sdk-patterns/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/plugins/saas-packs/clari-pack/skills/clari-sdk-patterns/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/plugins/saas-packs/clari-pack/skills/clari-sdk-patterns/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit"
    ],
    "license": "MIT"
  },
  "instructions": "# Clari SDK Patterns\n\n## Overview\n\nClari has no official SDK -- build typed wrappers around the v4 REST API. These patterns cover the Export API for forecasts, job polling, and data transformation pipelines.\n\n## Prerequisites\n\n- Completed `clari-install-auth` setup\n- Python 3.10+ (primary) or TypeScript 5+\n\n## Instructions\n\n### Step 1: Python Client\n\n```python\n# clari_client.py\nimport os\nimport time\nimport requests\nfrom dataclasses import dataclass, field\nfrom typing import Optional\n\n@dataclass\nclass ClariConfig:\n    api_key: str\n    base_url: str = \"https://api.clari.com/v4\"\n    poll_interval",
  "cost": {
    "context_tokens": 1729
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-clari-sdk-patterns/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.

clari-sdk-patterns - Skill - OpenSmartRoute