forgefy

Official Dart & Flutter SDK for the Forgefy Developer API — transcripts in, structured product requirements (features, open questions, conflicts, action items) out.

Pure Dart — works on the Dart VM, Flutter mobile/desktop, and Flutter web (depends only on package:http, never dart:io).

dependencies:
  forgefy: ^0.1.0

Quick start

import 'package:forgefy/forgefy.dart';

final forgefy = Forgefy(
  apiKey: 'fgy_live_...',            // from the dashboard's Developers page
  baseUrl: 'https://your-forgefy-host',
);

final result = await forgefy.extract(
  transcript: 'We need Google login before launch. Sarah owns billing.',
  extractors: [Extractor.features, Extractor.actionItems], // optional — omit for all four
);

for (final f in result.features) {
  print('[${f.priority}] ${f.title}');
}
print(result.usage.inputTokens); // token consumption

forgefy.close(); // release the HTTP client when done

Every model-generated payload keeps its full JSON on .raw, so fields not typed on Feature / Question / Conflict / ActionItem are still reachable.

Long transcripts (async jobs)

final job = await forgefy.jobs.create(
  transcript: longTranscript,                      // up to 200k chars
  webhookUrl: 'https://yourapp.com/hooks/forgefy', // optional
);

// Either wait by polling…
final done = await forgefy.jobs.waitFor(job.jobId); // throws JobFailedException / JobTimeoutException
print(done.result);

// …or verify the webhook delivery instead (see below).

jobs.create() sends an auto-generated Idempotency-Key, so a network-level retry can never run the same job twice. Pass idempotencyKey: yourself to dedupe across processes.

Webhooks

Deliveries are signed X-Forgefy-Signature: sha256=<hex>, HMAC-SHA256 over the raw body with the webhookSecret returned at job creation. Verify against the raw bytes, before parsing JSON:

import 'package:forgefy/forgefy.dart';

// rawBody: the exact bytes received; signature: the X-Forgefy-Signature header
if (!verifySignature(rawBody, signature, job.webhookSecret!)) {
  // reject with 401
}

Comparison is constant-time; malformed input returns false rather than throwing.

Errors

Every API failure throws a subclass of ForgefyException with .status and .detail:

Class When
AuthenticationException 401 — bad or revoked key
QuotaExceededException 402 — monthly tokens exhausted (free tier); detail has the reset date
NotFoundException 404
ValidationException 422
RateLimitException 429 — 60 req/min per key (retried automatically first)
ServerException 5xx
ApiConnectionException no HTTP response at all
JobFailedException / JobTimeoutException jobs.waitFor outcomes

Retry behavior mirrors the other SDKs: 429 and network errors are always retried (default 2 retries, exponential backoff with jitter, Retry-After honored). 5xx is retried only where safe — GETs and idempotent job creation, never the sync extract() (a retry would bill tokens twice).

Quota

final usage = await forgefy.usage();
// usage.tier, usage.monthlyTokens, usage.tokensUsed, usage.tokensRemaining, usage.resetsAt

Paid accounts over budget aren't blocked — requests are served by the free economy model instead. Check result.modelTier to see which tier answered.

Development

dart pub get
dart test        # all HTTP mocked
dart analyze

Libraries

forgefy
Official Dart & Flutter SDK for the Forgefy Developer API — transcripts in, structured product requirements (features, open questions, conflicts, action items) out.