Skip to content
Development
Skill

/output-dev-http-client-create

Create shared HTTP clients in src/shared/clients/ for Output SDK workflows. Use when integrating external APIs, creating service wrappers, or standardizing HTTP operations.

From plugin
output
44451 skills5 agents

Claude Code

$ npx -y skills add growthxai/output --skill output-dev-http-client-create --agent claude-code

SKILL.md

output-dev-http-client-create.SKILL.md
name: output-dev-http-client-create
description: Create shared HTTP clients in src/shared/clients/ for Output SDK workflows. Use when integrating external APIs, creating service wrappers, or standardizing HTTP operations.
allowed-tools: [Read, Write, Edit, Glob]

Creating HTTP Clients

Overview

This skill documents how to create shared HTTP clients for Output SDK workflows. Clients are stored in `src/shared/clients/` and shared across all workflows to ensure consistent error handling, retry logic, and API integration patterns.

When to Use This Skill

  • Integrating a new external API service
  • Creating a reusable HTTP wrapper for a service
  • Standardizing error handling for API calls
  • Moving inline HTTP logic to a shared client

Location Convention

HTTP clients are stored in the shared clients folder:

src/shared/clients/
├── gemini_client.ts     # Google Gemini API client
├── jina_client.ts       # Jina AI client
├── perplexity_client.ts # Perplexity API client
└── {service}_client.ts  # Your new client

**Important**: Clients are shared across ALL workflows. Do NOT create per-workflow HTTP clients.

Other Shared Code Locations

src/shared/
├── clients/     # API clients (this skill)
├── utils/       # Utility functions & helpers
├── services/    # Business logic services
├── steps/       # Shared step definitions (optional)
└── evaluators/  # Shared evaluators (optional)

Import Pattern in Workflows

Use relative imports from workflow files to shared clients:

// CORRECT - Relative path from workflow steps.ts
import { GeminiImageService } from '../../shared/clients/gemini_client.js';
import { parseResumeWithJina } from '../../shared/clients/jina_client.js';

// From shared steps (if used)
import { JinaClient } from '../clients/jina_client.js';

Critical Import Rules

HTTP Client Import

// CORRECT - Use @outputai/core/http wrapper
import { createKyClient } from '@outputai/core/http';

// WRONG - Never use axios directly
import axios from 'axios';

Error Types Import

// CORRECT - Import error types from @outputai/core
import { FatalError, ValidationError } from '@outputai/core';

// WRONG - Custom error classes
class MyCustomError extends Error { ... }

Credentials Import

// CORRECT - Use @outputai/core/credentials for secrets
import { credentials } from '@outputai/core/credentials';
const apiKey = credentials.require('service.api_key');

// WRONG - Never use process.env for secrets
const apiKey = process.env.SERVICE_API_KEY;

Cost Tracking Import

// CORRECT - Attach spend to paid API calls with @outputai/core/http
import { addRequestCost, createKyClient } from '@outputai/core/http';

// WRONG - Tracking cost yourself in a side table, or skipping it for paid APIs

Basic Client Structure

Simple Function-Based Client

import { FatalError, ValidationError } from '@outputai/core';
import { createKyClient } from '@outputai/core/http';
import { credentials } from '@outputai/core/credentials';

const API_KEY = credentials.require('service.api_key');
const BASE_URL = 'https://api.service.com';

const client = createKyClient({
  prefix: BASE_URL,
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    Accept: 'application/json'
  },
  timeout: 30000,
  retry: {
    limit: 3,
    statusCodes: [408, 429, 500, 502, 503, 504]
  }
});

/**
 * Fetch data from the service
 *
 * @param query - Search query string
 * @returns Processed response data
 * @throws {FatalError} If authentication fails or resource not found
 * @throws {ValidationError} If temporary error occurs
 */
export async function fetchServiceData(query: string): Promise<ServiceResponse> {
  const response = await client.get('endpoint', {
    searchParams: { q: query }
  });

  const data = await response.json();

  if (!data.results) {
    throw new FatalError('No results returned from service');
  }

  return data;
}

Class-Based Client

import { FatalError, ValidationError } from '@outputai/core';
import { createKyClient } from '@outputai/core/http';
import { credentials } from '@outputai/core/credentials';

export interface ServiceOptions {
  model?: string;
  timeout?: number;
}

export class ServiceClient {
  private readonly client: ReturnType<typeof createKyClient>;
  private readonly model: string;

  constructor(apiKey?: string) {
    const key = apiKey ?? credentials.require('service.api_key');

    this.client = createKyClient({
      prefix: 'https://api.service.com',
      headers: {
        Authorization: `Bearer ${key}`,
        'Content-Type': 'application/json'
      },
      timeout: 30000,
      retry: {
        limit: 3,
        statusCodes: [408, 429, 500, 502, 503, 504]
      }
    });

    this.model = 'default-model';
  }

  async process(input: ProcessInput): Promise<ProcessOutput> {
    try {
      const response = await this.client.post('process', {
        json: {
          model: this.model,
          input
        }
      });

      return await response.json();
    } catch (error: unknown) {
      const err = error as { status?: number; message?: string };

      if (err.status === 429) {
        throw new ValidationError(`Rate limit exceeded: ${err.message}`);
      }

      if (err.status === 401 || err.status === 403) {
        throw new FatalError(`Authentication failed: ${err.message}`);
      }

      throw new ValidationError(`Service call failed: ${err.message}`);
    }
  }
}

Real-World Examples

Example 1: Jina Client (Function-Based)

import { FatalError } from '@outputai/core';
import { createKyClient } from '@outputai/core/http';
import { credentials } from '@outputai/core/credentials';

const JINA_API_KEY = credentials.require('jina.api_key');
const JINA_BASE_URL = 'https://r.jina.ai';

const client = createKyClient({
  prefix: JINA_BASE_URL,
  headers: {
    Authorizati
Read more
@growthxai
Ships withoutput

The open-source TypeScript framework for building AI workflows and agents. Designed for Claude Code — describe what you want, Claude builds it, with all the best practices already in place. One framework.

44451 skills5 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
444
Star history of growthxai/output on GitHub, a chart by star-history.com

Other skills on output.

output-build-workflow

output-build-workflow

Implement an Output SDK workflow from a plan document. Use when the user asks to build, implement, or code a workflow from an…

@growthxai@growthxaiInstall Skill
output-credentials-edit

output-credentials-edi…

View, edit, and set encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying…

@growthxai@growthxaiInstall Skill
output-credentials-init

output-credentials-ini…

Initialize encrypted credentials for an Output.ai project. Use when setting up credentials for the first time, adding…

@growthxai@growthxaiInstall Skill
output-debug-workflow

output-debug-workflow

Debug Output SDK workflow issues. Use when user reports a workflow failing, erroring, hanging, producing wrong results, or asks…

@growthxai@growthxaiInstall Skill
output-dev-agent-class

output-dev-agent-class

Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when…

@growthxai@growthxaiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about output-dev-http-client-create.

Which coding agents can use output-dev-http-client-create?
output-dev-http-client-create installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.