SDKs

Official AppForges TypeScript, Python and Go SDKs, plus the OpenAPI specification.

TypeScript SDK

@appforges/sdk@v0.59.4

The official SDK, generated automatically from the OpenAPI spec by the sdk-gen pipeline: zero runtime dependencies, dual ESM/CJS formats, typed errors, cursor-pagination iterators and SSE streaming out of the box. Response fields are wire-faithful (snake_case) — payloads pass through untouched.

Install

npm i @appforges/sdk
# or
bun add @appforges/sdk

Quick start

import { AppForges } from "@appforges/sdk";
import { BearerAuth } from "@appforges/sdk/runtime/auth/bearer";

const client = new AppForges({
  auth: new BearerAuth(process.env.APPFORGES_ACCESS_TOKEN!),
});

// Response fields are wire-faithful (snake_case) — payloads pass through.
const profile = await client.users.getProfile();
console.log(profile.user_id, profile.phone_number);

Paginating

// Cursor-paginated list methods return a Paginator:
for await (const page of client.pinpagePages.listPages(profile.user_id, { query: { pageSize: 50 } })) {
  // ...one item at a time
}
// or collect everything:
const all = await client.pinpagePages.listPages(profile.user_id).all();

Error handling

import { AppForgesError } from "@appforges/sdk/runtime/errors";

try {
  await client.users.getProfile();
} catch (error) {
  if (error instanceof AppForgesError) {
    console.error(error.httpStatus, error.requestId, error.retryAfterMs);
  }
}

AppForgesError carries httpStatus, requestId and retryAfterMs. Use reason and traceId to investigate requests.

Python SDK

appforges@0.59.4

Python 3.11+ with synchronous and asynchronous clients, strict pydantic models, OAuth, typed errors, cursor iteration and SSE. Wire fields retain snake_case. Runtime dependencies are httpx and pydantic v2.

Install

pip install appforges==0.59.4

Synchronous client

import os
from appforges import AppForges, BearerAuth

with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
    profile = client.users.get_profile()
    print(profile.user_id)

Asynchronous client

import asyncio
import os
from appforges import AsyncAppForges, BearerAuth

async def main() -> None:
    async with AsyncAppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
        profile = await client.users.get_profile()
        print(profile.user_id)

asyncio.run(main())

The profile endpoint requires an OAuth access token representing a user. For staging, set base_url="https://api-staging.appforges.com". The with / async with contexts close client-owned connections.

SSE streaming

For APIs that declare SSE, iterate over events with for or async for. The current public specification does not declare an SSE endpoint, so no endpoint example is available yet.

Cursor iteration

with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
    profile = client.users.get_profile()
    for page in client.pinpage_pages.list_pages_all(profile.user_id, page_size=50, max_pages=10):
        print(page.page_id)

# AsyncAppForges exposes the same iterator through async for.

Error handling

from appforges import APIError, RateLimitExceededError

with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
    try:
        client.users.get_profile()
    except RateLimitExceededError as error:
        print(error.retry_after)  # seconds
    except APIError as error:
        print(error.http_status, error.reason, error.request_id, error.trace_id)

At most two retries by default, honoring Retry-After. Writes retry only under an idempotency contract. APIError retains status, reason, request_id and trace_id. Omitted fields are not sent; explicit None is accepted only for nullable fields.

Go SDK

appforges-sdk-go@v0.59.4

Go 1.23+ with the standard library and no third-party runtime dependencies. Context cancellation, strict models, OAuth, typed errors, cursor pagination, and SSE; JSON fields retain snake_case names.

Install

go get github.com/appforges/appforges-sdk-go@v0.59.4

Quick start

package main

import (
    "context"
    "fmt"
    "os"
    "time"
    appforges "github.com/appforges/appforges-sdk-go"
    "github.com/appforges/appforges-sdk-go/runtime"
)

func main() {
    client, err := appforges.NewClient(runtime.WithBearerToken(os.Getenv("APPFORGES_ACCESS_TOKEN")))
    if err != nil { panic(err) }
    defer client.Close()
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    profile, err := client.Users.GetProfile(ctx)
    if err != nil { panic(err) }
    fmt.Println(runtime.Value(profile.UserId))
}

The profile API needs an OAuth access token representing a user. For staging, add runtime.WithBaseURL("https://api-staging.appforges.com"). Close releases idle connections owned by the client.

Pagination

for page, err := range client.PinpagePages.ListPagesAll(ctx, appforges.PinpagePagesListPagesParams{
    UserId: runtime.Value(profile.UserId),
    PageSize: runtime.Some[int64](50),
}) {
    if err != nil { return err }
    fmt.Println(runtime.Value(page.PageId))
    // break stops fetching further pages.
}

Error handling

import "errors"

var limited *appforges.RateLimitExceededError
var apiError *appforges.APIError
if errors.As(err, &limited) {
    fmt.Println(limited.RetryAfter)
} else if errors.As(err, &apiError) {
    fmt.Println(apiError.StatusCode, apiError.Reason, apiError.RequestID, apiError.TraceID)
}

Up to two retries by default, honoring Retry-After. Writes follow the idempotency contract. errors.As exposes status, reason, request_id, and trace_id. Optional[T] with Some / Null distinguishes absence, values, and explicit null on nullable fields.

SSE streaming

APIs declaring SSE return iter.Seq2; break or context cancellation closes the response connection. The current public specification has no SSE endpoints, so there is no live endpoint example.

File data and Webhook verification

encodeUploadData / encode_upload_data / EncodeUploadData encode binary files smaller than 4 MiB for the public UploadAsset JSON base64 field. Helpers send no requests. The pre-signed upload flow is not currently public.

TypeScript

import { encodeUploadData } from "@appforges/sdk/runtime/upload";
import { verifyWebhookSignature } from "@appforges/sdk/runtime/webhook";

const data = encodeUploadData(new Uint8Array([0, 255]));
// Pass data to the generated UploadAsset request body.
async function verifyRequest(request: Request, originalSecret: string) {
  const body = new Uint8Array(await request.arrayBuffer());
  const signature = request.headers.get("X-Webhook-Signature") ?? "";
  return verifyWebhookSignature(body, signature, [originalSecret]);
}

Python

from appforges import encode_upload_data, verify_webhook_signature

data = encode_upload_data(bytes([0, 255]))

def verify_request(raw_body: bytes, signature: str, original_secret: str) -> bool:
    return verify_webhook_signature(raw_body, signature, [original_secret])

Go

import "github.com/appforges/appforges-sdk-go/runtime"

func encodeFile(data []byte) (string, error) {
    return runtime.EncodeUploadData(data)
}
func verifyRequest(rawBody []byte, signature, originalSecret string) bool {
    return runtime.VerifyWebhookSignature(rawBody, signature, []string{originalSecret})
}

Verify X-Webhook-Signature using raw body bytes before JSON parsing and the original secret returned by create or rotate. Multiple local secrets support rotation transitions. The current protocol does not prevent replay or authenticate X-Webhook-Event.

SDK releases include API compatibility checks and an automatically generated CHANGELOG. Model, field and method documentation follows the OpenAPI specification.

OpenAPI specification

Prefer to generate your own client? Download the OpenAPI 3.1 spec and feed it into your favorite generator.

Current version v0.59.4, regenerated on every platform release.

Versioning

The wire schema your client speaks is controlled by the API-Version Versioning and upgrades header — see the dedicated guide.