SDK · Swift

Build on Bequest, in Swift.

Idiomatic client for the Bequest API. Same endpoints as REST, same idempotency, native error types.

Install

.package(url: "https://github.com/bequest/bequest-swift.git", from: "1.0.0")

Runtime

Swift 5.9+ / iOS 16 / macOS 13

Auth

Bearer token via BQ_KEY env var.

1. Initialize

Authenticate once.

import Bequest

let bq = Bequest(apiKey: ProcessInfo.processInfo.environment["BQ_KEY"]!)
2. Send a gift

One call. One dollar.

let gift = try await bq.gifts.create(
    .init(
        from: "usr_8f3...",
        to: "org_a91...",
        amountCents: 2500,
        memo: "Spring drive"
    ),
    idempotencyKey: requestID
)
print(gift.status)  // .settling
3. Open a campaign

Hosted page in one POST.

let campaign = try await bq.campaigns.create(
    .init(
        name: "Spring Build-a-Well Drive",
        goalCents: 7_500_000,
        story: "We are building 12 wells in...",
        status: .live
    )
)
print(campaign.url)
4. Open a pool

Standing commitments, batched annually.

let pool = try await bq.pools.create(
    .init(
        name: "Cedar Block Mutual Aid",
        flavor: .community,
        legalStructure: .mutualAid,
        distributionMechanism: .threshold
    )
)

try await bq.pools.disburse(pool.id, to: "usr_77...", amountCents: 35_000)
5. Listen for webhooks

Signed payloads, replay-safe.

// Server-side Swift (Vapor)
app.post("webhooks", "bequest") { req async throws -> HTTPStatus in
    let payload = try req.body.collect().unwrap()
    let sig     = req.headers["bq-signature"].first ?? ""

    let event = try Bequest.Webhooks.constructEvent(
        payload: payload,
        signature: sig,
        secret: Environment.get("BQ_WEBHOOK_SECRET")!
    )

    if event.type == "gift.settled" {
        try await Ledger.write(event.data)
    }
    return .ok
}
6. Handle errors

Typed by category.

do {
    let gift = try await bq.gifts.create(/* ... */)
} catch let error as BequestError {
    switch error {
    case .idempotency:  break    // reuse existing
    case .rateLimit(let after): try await Task.sleep(for: .seconds(after))
    case .auth:         break    // rotate key
    default: throw error
    }
}
Reference

Full API docs.

The complete reference covers every resource, every parameter, every status code.

Open the API reference