Fastbound
A Ruby gem for the FastBound firearms compliance API.
Installation
Add to your Gemfile:
gem "fastbound"
Or install directly:
gem install fastbound
Configuration
client = Fastbound::Client.new(
api_key: "your_api_key",
account_number: "A001234",
audit_user: "user@example.com" # required for create/update/delete operations
)
| Option | Required | Description |
|---|---|---|
api_key |
Yes | Your FastBound API key (used as HTTP Basic auth username) |
account_number |
Yes | Your FastBound account number |
audit_user |
No* | Email address recorded on write operations. Required by the API for POST/PUT/DELETE. |
base_url |
No | Override the API base URL (default: https://cloud.fastbound.com) |
Resources
Account
client.account.get
Acquisitions
# List acquisitions
client.acquisitions.list(take: 25, skip: 0)
client.acquisitions.list(type: "Purchase", acquired_from_contact_id: "uuid")
# Find by ID or external ID
client.acquisitions.find("uuid")
client.acquisitions.find_by_external_id("my-ext-id")
# Create
client.acquisitions.create(
date: "2024-01-15",
type: "Purchase",
invoice_number: "INV-001",
external_id: "my-acq-001"
)
# Update
client.acquisitions.update("uuid", invoice_number: "INV-002")
# Delete
client.acquisitions.destroy("uuid")
# Attach a contact
client.acquisitions.attach_contact("acquisition-uuid", "contact-uuid")
# Commit
client.acquisitions.commit("uuid")
client.acquisitions.commit("uuid", list_acquired_items: true)
# Create and commit in one request
client.acquisitions.create_and_commit(
date: "2024-01-15",
type: "Purchase",
contact_id: "contact-uuid",
items: [{ manufacturer: "Glock", model: "19", serial: "ABC123", type: "Pistol", caliber: "9mm" }]
)
# Create as pending (does not commit)
client.acquisitions.create_as_pending(
date: "2024-01-15",
type: "Purchase",
contact_id: "contact-uuid",
items: [{ manufacturer: "Glock", model: "19", serial: "ABC123", type: "Pistol", caliber: "9mm" }]
)
# Items
client.acquisitions.get_item("acquisition-uuid", "item-uuid")
client.acquisitions.add_item("acquisition-uuid", manufacturer: "Glock", model: "19", serial: "ABC123", type: "Pistol", caliber: "9mm")
client.acquisitions.add_items("acquisition-uuid", [
{ manufacturer: "Glock", model: "19", serial: "ABC123", type: "Pistol", caliber: "9mm" },
{ manufacturer: "Glock", model: "17", serial: "DEF456", type: "Pistol", caliber: "9mm" }
])
client.acquisitions.update_item("acquisition-uuid", "item-uuid", location: "Safe A")
client.acquisitions.delete_item("acquisition-uuid", "item-uuid")
Attachments
# Returns raw binary data
pdf_bytes = client..download("attachment-uuid")
File.binwrite("attachment.pdf", pdf_bytes)
Contacts
# List contacts
client.contacts.list(take: 25, skip: 0)
client.contacts.list(last_name: "Smith", ffl_number: "1-23-456-07-8A-12345")
# Find by ID or external ID
client.contacts.find("uuid")
client.contacts.find_by_external_id("my-ext-id")
# Create
client.contacts.create(
first_name: "John",
last_name: "Smith",
ffl_number: "1-23-456-07-8A-12345",
ffl_expires: "2026-12-31",
premise_address1: "123 Main St",
premise_city: "Anytown",
premise_state: "TX",
premise_zip_code: "75001"
)
# Update
client.contacts.update("uuid", email_address: "john@example.com")
# Delete
client.contacts.destroy("uuid")
# Merge two contacts (winning contact receives all data from losing contact)
client.contacts.merge(winning_contact_id: "uuid-1", losing_contact_id: "uuid-2")
# Licenses
client.contacts.get_license("contact-uuid", "license-uuid")
client.contacts.create_license("contact-uuid", type: "FFL", number: "1-23-456-07-8A-12345", expiration: "2026-12-31")
client.contacts.update_license("contact-uuid", "license-uuid", copy_on_file: true)
client.contacts.delete_license("contact-uuid", "license-uuid")
Dispositions
# List dispositions
client.dispositions.list(take: 25, skip: 0)
client.dispositions.list(type: "SaleTo", disposed_to_contact_id: "uuid")
client.dispositions.list(include_4473: true)
# List Form 4473 dispositions
client.dispositions.list_4473s(take: 25)
client.dispositions.list_4473s(include_awaiting_completion: true)
# Find by ID or external ID
client.dispositions.find("uuid")
client.dispositions.find_by_external_id("my-ext-id")
# Create standard disposition
client.dispositions.create(type: "SaleTo", date: "2024-01-15", generate_ttsn: true)
# Create special disposition types
client.dispositions.create_nfa(date: "2024-01-15", type: "SaleTo", submission_date: "2024-01-10")
client.dispositions.create_theft_loss(date: "2024-01-15", theft_loss__type: "Theft")
client.dispositions.create_destroyed(date: "2024-01-15", destroyed__description: "Destroyed per ATF")
# Update
client.dispositions.update("uuid", note: "Updated note")
# Delete
client.dispositions.destroy("uuid")
# Attach a contact
client.dispositions.attach_contact("disposition-uuid", "contact-uuid")
# Lock
client.dispositions.lock("uuid")
client.dispositions.lock_by_external_id("my-ext-id")
# Commit
client.dispositions.commit("uuid")
client.dispositions.commit("uuid", list_disposed_items: true)
# Create and commit in one request
client.dispositions.create_and_commit(
type: "SaleTo",
date: "2024-01-15",
contact_id: "contact-uuid",
items: [{ id: "item-uuid", price: 500.00 }]
)
# Create as pending (does not commit)
client.dispositions.create_as_pending(
type: "SaleTo",
date: "2024-01-15",
contact_id: "contact-uuid",
items: [{ id: "item-uuid", price: 500.00 }]
)
# Items
client.dispositions.list_items("disposition-uuid")
client.dispositions.add_items("disposition-uuid", [{ id: "item-uuid", price: 500.00 }])
client.dispositions.add_items_by_external_id("disp-ext-id", [{ external_id: "item-ext-id", price: 500.00 }])
client.dispositions.add_items_by_search("disp-ext-id", manufacturer: "Glock", model: "19", caliber: "9mm")
client.dispositions.edit_item_price("disposition-uuid", "item-uuid", price: 550.00)
client.dispositions.remove_item("disposition-uuid", "item-uuid")
client.dispositions.remove_item_by_external_id("disposition-uuid", "item-ext-id")
Downloads
# Download bound book as binary (PDF or CSV depending on API response)
data = client.downloads.bound_book
File.binwrite("bound_book.pdf", data)
Form 4473s
# Returns raw binary PDF data
pdf_bytes = client.form4473s.download("form4473-uuid")
File.binwrite("form4473.pdf", pdf_bytes)
Inventory
# Bulk verify inventory by serial number
result = client.inventory.bulk_verify(
serials: ["ABC123", "DEF456", "GHI789"],
rollback_partial: true,
update_location: true,
location: "Safe A",
verified_utc: "2024-01-15T10:00:00Z"
)
Items
# List items (extensive filtering available)
client.items.list(take: 25, skip: 0)
client.items.list(manufacturer: "Glock", caliber: "9mm", status: 1)
client.items.list(serial: "ABC123")
client.items.list(acquired_on_or_after: "2024-01-01", acquired_on_or_before: "2024-12-31")
client.items.list(is_theft_loss: false, is_destroyed: false)
# Find by ID or external ID
client.items.find("uuid")
client.items.find_by_external_id("my-ext-id")
# Update
client.items.update("uuid", location: "Safe B", note: "Cleaned and inspected")
# Delete (logical delete with type)
client.items.delete("uuid", delete_type: "Destroyed", delete_note: "Per ATF guidelines")
# Set acquisition contact
client.items.set_acquisition_contact("item-uuid", "contact-uuid")
# Undispose
client.items.undispose("uuid")
client.items.undispose("uuid", note: "Returned by customer")
# Set external IDs
client.items.set_external_id("uuid", external_id: "my-item-001")
client.items.set_external_ids([
{ id: "uuid-1", external_id: "my-item-001" },
{ id: "uuid-2", external_id: "my-item-002" }
])
Multiple Sale Reports
# Returns raw binary PDF data
pdf_bytes = client.multiple_sale_reports.download("report-uuid", "attachment-uuid")
File.binwrite("multiple_sale_report.pdf", pdf_bytes)
Smart Lists
Smart lists return the allowed values for various fields in your account.
client.smart_lists.acquire_types
client.smart_lists.calibers
client.smart_lists.conditions
client.smart_lists.countries_of_manufacture
client.smart_lists.delete_types
client.smart_lists.dispose_types
client.smart_lists.importers
client.smart_lists.item_types
client.smart_lists.license_types
client.smart_lists.locations
client.smart_lists.manufacturers
client.smart_lists.theft_loss_types
client.smart_lists.manufacturing_dispose_types
client.smart_lists.manufacturing_acquire_types
Users
client.users.list
client.users.list(include_disabled: true)
Webhooks
# List available webhook events
client.webhooks.list_events
# Find a webhook by name
client.webhooks.find("my-webhook")
# Create a webhook
client.webhooks.create(
name: "my-webhook",
url: "https://example.com/webhooks/fastbound",
description: "Acquisition notifications",
events: ["acquisition.committed", "disposition.committed"]
)
# Update a webhook
client.webhooks.update("my-webhook", url: "https://example.com/webhooks/v2")
# Delete a webhook
client.webhooks.destroy("my-webhook")
Error Handling
begin
client.contacts.find("nonexistent-uuid")
rescue Fastbound::NotFoundError => e
puts "Not found: #{e.}"
rescue Fastbound::UnprocessableEntityError => e
puts "Validation failed: #{e.errors.join(", ")}"
rescue Fastbound::UnauthorizedError => e
puts "Auth error: #{e.}"
rescue Fastbound::ApiError => e
puts "API error #{e.status}: #{e.}"
end
| Exception | HTTP Status |
|---|---|
Fastbound::UnauthorizedError |
401, 403 |
Fastbound::NotFoundError |
404 |
Fastbound::UnprocessableEntityError |
400, 422 |
Fastbound::ApiError |
All other errors |
License
MIT