Swift SDK
vendo-sdk-swift v1.0.0 — install, public surface, and reference links.
Package
| Package | vendo-sdk-swift |
| Version | 1.0.0 |
| Source | github.com/runvendo/vendo-sdk-swift |
| Changelog | CHANGELOG.md |
| Platforms | iOS 15+, macOS 12+, tvOS 15+, watchOS 8+ |
| Requires | Swift 5.9+ |
Install
In Package.swift:
.package(
url: "https://github.com/runvendo/vendo-sdk-swift",
from: "1.0.0"
)Then add "Vendo" to your target's dependencies.
Public surface
Vendo actor
Vendo is implemented as a Swift actor (not a class). The constructor
throws VendoError.auth if VENDO_API_KEY is unset and no apiKey: is
passed.
import Vendo
// Vendo mode (reads VENDO_API_KEY from env)
let vendo = try Vendo()
// OSS/BYOK mode — pass an explicit key (any non-empty string opts into the
// instance; per-slug BYOK lookups read provider-conventional env vars).
let vendo = try Vendo(apiKey: "byok")
// Explicit — note no `/api` suffix on baseURL.
let vendo = try Vendo(
apiKey: "vendo_sk_...",
baseURL: "https://vendo.run"
)Sub-API properties are nonisolated, so synchronous access does not require
await. Async methods on the actor (e.g. token, forUser) do.
Token methods
try await vendo.token(_ slug: String) -> String
try await vendo.tokens(_ slugs: [String]) -> [String: String?]
await vendo.invalidate(_ slug: String)Scoping
try await vendo.forUser(jwt: String) -> Vendo
try await vendo.forRequest(headers: [String: String]) -> Vendo // Vendo mode onlyConnect URL
try await vendo.connectURL(
slug: String,
returnTo: String? = nil,
state: String? = nil
) -> URL // Vendo mode onlySub-APIs (nonisolated)
vendo.connections // ConnectionsAPI
vendo.integrations // IntegrationsAPI
vendo.billing // BillingAPI (Vendo mode only)
vendo.events // EventsAPI (Vendo mode only)
vendo.webhooks // WebhooksAPI (both modes)ConnectionsAPI
try await vendo.connections.list() -> [Connection]
try await vendo.connections.get(slug: String) -> Connection?IntegrationsAPI
try await vendo.integrations.list() -> [Integration]
try await vendo.integrations.get(slug: String) -> Integration?
vendo.integrations.envVars(for slug: String) -> [String] // no network callBillingAPI (Vendo mode only)
try await vendo.billing.balance() -> Balance
try await vendo.billing.spendCaps() -> SpendCaps
try await vendo.billing.usage(period: BillingPeriod = .month) -> AnyCodableSpendCaps is a single struct holding all four micros fields; it is not an
array. usage(period:) returns the raw JSON wrapped in AnyCodable — there
is no Usage type in this SDK.
EventsAPI (Vendo mode only)
let stream = try vendo.events.subscribe()
// stream is an EventsAsyncSequence; iterating yields EventStreamMessage.
for try await event in stream {
print(event.type, event.data)
}subscribe() reconnects on transient errors with exponential backoff capped
at 30 s (override via maxBackoffSeconds:).
WebhooksAPI
maxAgeSec is a constructor parameter on WebhooksAPI — verify(...)
takes only headers and body.
let webhooks = WebhooksAPI(secret: "whsec_...", maxAgeSeconds: 300)
let event: WebhookEvent = try webhooks.verify(
headers: ["Vendo-Signature": "...", "Vendo-Timestamp": "..."],
body: rawBodyString
)When constructed without secret:, the verifier reads
VENDO_WEBHOOK_SECRET from ProcessInfo.processInfo.environment.
BYOK helpers
import Vendo
BYOK.primaryEnvVar(for: "openai") // "OPENAI_API_KEY"
BYOK.allEnvVars(for: "slack") // ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"]
BYOK.isOAuthSlug("google") // Bool
BYOK.knownSlugs() // Set<String>VendoError enum
VendoError is an enum; every case uses labeled associated values.
public enum VendoError: Error, Sendable {
case auth(message: String, code: String)
case notConnected(slug: String?, message: String)
case needsReauth(slug: String?, message: String, connectURL: String?)
case balanceExhausted(message: String)
case spendCapExceeded(message: String, retryAfter: Int?)
case rateLimited(retryAfter: Int?)
case upstream(status: Int, message: String)
case validation(message: String)
case idempotencyConflict(message: String)
case vendoOnlyFeature(featureName: String)
case identityNotPresent(message: String)
case other(code: String, message: String, status: Int?)
}Use VendoError.from(status:headers:body:) to convert a raw HTTP response
into the right case.
See Concepts: Errors for usage.
The Swift SDK is the only one that exposes a synchronous envVars(for:) method on IntegrationsAPI — it reads from the bundled Resources/byok.json catalog and requires no network call.