Rundunrundun

SDKs

There are no official Rundun SDKs yet. The REST API is designed to be simple to call directly from any language — no SDK required for most integrations.

Planned SDKs

The following SDKs are on the roadmap:

  • Node.js / TypeScript — typed client wrapping the REST API, matching the response shapes in the API reference
  • Python — for automation workflows and data pipelines

No release timeline is set. The REST API is stable and complete — you can build a production integration today without waiting for an SDK.

Calling the API directly

The API uses standard HTTP with JSON request and response bodies. Any HTTP client works.

Node.js with fetch

const RUNDUN_API_KEY = process.env.RUNDUN_API_KEY!
const BASE_URL = 'https://api.rundun.app/v1'

async function dispatchRun(templateId: string, webhookUrl: string, webhookSecret: string) {
  const res = await fetch(`${BASE_URL}/runs`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${RUNDUN_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      template_id: templateId,
      webhook: {
        url: webhookUrl,
        secret: webhookSecret,
        events: ['run.completed', 'run.cancelled', 'run.expired'],
      },
      expires_in_hours: 24,
    }),
  })

  if (!res.ok) {
    const error = await res.json()
    throw new Error(`${res.status}: ${error.message}`)
  }

  return res.json() as Promise<{ run_id: string; link: string; expires_at: string }>
}

async function getRun(runId: string) {
  const res = await fetch(`${BASE_URL}/runs/${runId}`, {
    headers: { 'Authorization': `Bearer ${RUNDUN_API_KEY}` },
  })
  if (!res.ok) throw new Error(`Failed to fetch run: ${res.status}`)
  return res.json()
}

Node.js with axios

import axios from 'axios'

const client = axios.create({
  baseURL: 'https://api.rundun.app/v1',
  headers: { Authorization: `Bearer ${process.env.RUNDUN_API_KEY}` },
})

const { data } = await client.post('/runs', {
  template_id: 't-550e8400-e29b-41d4-a716-446655440000',
  webhook: {
    url: 'https://your-app.com/hooks/rundun',
    secret: process.env.RUNDUN_WEBHOOK_SECRET,
    events: ['run.completed'],
  },
  expires_in_hours: 48,
})

console.log(data.link) // https://rundun.app/r/r-...

Python with requests

import os
import requests

RUNDUN_API_KEY = os.environ['RUNDUN_API_KEY']
BASE_URL = 'https://api.rundun.app/v1'

session = requests.Session()
session.headers.update({'Authorization': f'Bearer {RUNDUN_API_KEY}'})

def dispatch_run(template_id: str, webhook_url: str, webhook_secret: str) -> dict:
    res = session.post(f'{BASE_URL}/runs', json={
        'template_id': template_id,
        'webhook': {
            'url': webhook_url,
            'secret': webhook_secret,
            'events': ['run.completed', 'run.cancelled'],
        },
        'expires_in_hours': 24,
    })
    res.raise_for_status()
    return res.json()

def get_run(run_id: str) -> dict:
    res = session.get(f'{BASE_URL}/runs/{run_id}')
    res.raise_for_status()
    return res.json()

# Example usage
run = dispatch_run(
    template_id='t-550e8400-e29b-41d4-a716-446655440000',
    webhook_url='https://your-app.com/hooks/rundun',
    webhook_secret=os.environ['RUNDUN_WEBHOOK_SECRET'],
)
print(run['link'])

Python with httpx (async)

import os
import httpx

async def dispatch_run(template_id: str) -> dict:
    async with httpx.AsyncClient(
        base_url='https://api.rundun.app/v1',
        headers={'Authorization': f'Bearer {os.environ["RUNDUN_API_KEY"]}'},
    ) as client:
        res = await client.post('/runs', json={
            'template_id': template_id,
            'webhook': {
                'url': 'https://your-app.com/hooks/rundun',
                'secret': os.environ['RUNDUN_WEBHOOK_SECRET'],
                'events': ['run.completed'],
            },
            'expires_in_hours': 24,
        })
        res.raise_for_status()
        return res.json()

Building your own client wrapper

For a real integration you will likely want a thin wrapper that handles authentication, base URL, and error mapping. A minimal TypeScript client:

class RundunClient {
  private readonly headers: Record<string, string>

  constructor(private readonly apiKey: string) {
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    }
  }

  async post<T>(path: string, body: unknown): Promise<T> {
    const res = await fetch(`https://api.rundun.app/v1${path}`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(body),
    })
    if (!res.ok) {
      const err = await res.json().catch(() => ({ message: res.statusText }))
      throw Object.assign(new Error(err.message), { status: res.status, code: err.error })
    }
    return res.json()
  }

  async get<T>(path: string): Promise<T> {
    const res = await fetch(`https://api.rundun.app/v1${path}`, { headers: this.headers })
    if (!res.ok) {
      const err = await res.json().catch(() => ({ message: res.statusText }))
      throw Object.assign(new Error(err.message), { status: res.status, code: err.error })
    }
    return res.json()
  }
}

// Usage
const rundun = new RundunClient(process.env.RUNDUN_API_KEY!)
const run = await rundun.post('/runs', { template_id: 't-...', expires_in_hours: 24 })

See also