Module: Basecamp

Defined in:
lib/basecamp.rb,
lib/basecamp/http.rb,
lib/basecamp/error.rb,
lib/basecamp/hooks.rb,
lib/basecamp/oauth.rb,
lib/basecamp/client.rb,
lib/basecamp/config.rb,
lib/basecamp/version.rb,
lib/basecamp/security.rb,
lib/basecamp/api_error.rb,
lib/basecamp/exit_code.rb,
lib/basecamp/auth_error.rb,
lib/basecamp/error_code.rb,
lib/basecamp/noop_hooks.rb,
lib/basecamp/oauth/pkce.rb,
lib/basecamp/bearer_auth.rb,
lib/basecamp/chain_hooks.rb,
lib/basecamp/oauth/token.rb,
lib/basecamp/usage_error.rb,
lib/basecamp/logger_hooks.rb,
lib/basecamp/oauth/config.rb,
lib/basecamp/request_info.rb,
lib/basecamp/auth_strategy.rb,
lib/basecamp/network_error.rb,
lib/basecamp/oauth/fetcher.rb,
lib/basecamp/oauth/exchange.rb,
lib/basecamp/oauth/resource.rb,
lib/basecamp/operation_info.rb,
lib/basecamp/request_result.rb,
lib/basecamp/token_provider.rb,
lib/basecamp/webhooks/event.rb,
lib/basecamp/ambiguous_error.rb,
lib/basecamp/download_result.rb,
lib/basecamp/forbidden_error.rb,
lib/basecamp/generated/types.rb,
lib/basecamp/not_found_error.rb,
lib/basecamp/oauth/discovery.rb,
lib/basecamp/webhooks/verify.rb,
lib/basecamp/operation_result.rb,
lib/basecamp/rate_limit_error.rb,
lib/basecamp/validation_error.rb,
lib/basecamp/oauth/oauth_error.rb,
lib/basecamp/webhooks/receiver.rb,
lib/basecamp/oauth_token_provider.rb,
lib/basecamp/oauth/refresh_request.rb,
lib/basecamp/static_token_provider.rb,
lib/basecamp/oauth/discovery_result.rb,
lib/basecamp/oauth/exchange_request.rb,
lib/basecamp/webhooks/rack_middleware.rb,
lib/basecamp/services/todos_extensions.rb,
lib/basecamp/webhooks/verification_error.rb,
lib/basecamp/services/authorization_service.rb,
lib/basecamp/generated/services/base_service.rb,
lib/basecamp/oauth/discovery_selection_error.rb,
lib/basecamp/generated/services/cards_service.rb,
lib/basecamp/generated/services/todos_service.rb,
lib/basecamp/generated/services/tools_service.rb,
lib/basecamp/generated/services/boosts_service.rb,
lib/basecamp/generated/services/events_service.rb,
lib/basecamp/generated/services/gauges_service.rb,
lib/basecamp/generated/services/lineup_service.rb,
lib/basecamp/generated/services/people_service.rb,
lib/basecamp/generated/services/search_service.rb,
lib/basecamp/generated/services/vaults_service.rb,
lib/basecamp/oauth/protected_resource_metadata.rb,
lib/basecamp/generated/services/account_service.rb,
lib/basecamp/generated/services/reports_service.rb,
lib/basecamp/generated/services/uploads_service.rb,
lib/basecamp/generated/services/checkins_service.rb,
lib/basecamp/generated/services/comments_service.rb,
lib/basecamp/generated/services/forwards_service.rb,
lib/basecamp/generated/services/messages_service.rb,
lib/basecamp/generated/services/projects_service.rb,
lib/basecamp/generated/services/timeline_service.rb,
lib/basecamp/generated/services/todosets_service.rb,
lib/basecamp/generated/services/webhooks_service.rb,
lib/basecamp/generated/services/campfires_service.rb,
lib/basecamp/generated/services/documents_service.rb,
lib/basecamp/generated/services/schedules_service.rb,
lib/basecamp/generated/services/templates_service.rb,
lib/basecamp/generated/services/todolists_service.rb,
lib/basecamp/generated/services/wormholes_service.rb,
lib/basecamp/generated/services/automation_service.rb,
lib/basecamp/generated/services/card_steps_service.rb,
lib/basecamp/generated/services/recordings_service.rb,
lib/basecamp/generated/services/timesheets_service.rb,
lib/basecamp/generated/services/attachments_service.rb,
lib/basecamp/generated/services/card_tables_service.rb,
lib/basecamp/generated/services/hill_charts_service.rb,
lib/basecamp/generated/services/card_columns_service.rb,
lib/basecamp/generated/services/message_types_service.rb,
lib/basecamp/generated/services/subscriptions_service.rb,
lib/basecamp/generated/services/client_replies_service.rb,
lib/basecamp/generated/services/message_boards_service.rb,
lib/basecamp/generated/services/my_assignments_service.rb,
lib/basecamp/generated/services/todolist_groups_service.rb,
lib/basecamp/generated/services/client_approvals_service.rb,
lib/basecamp/generated/services/my_notifications_service.rb,
lib/basecamp/generated/services/client_visibility_service.rb,
lib/basecamp/generated/services/client_correspondences_service.rb

Overview

Main entry point for the Basecamp SDK.

The SDK follows a Client -> AccountClient pattern:

  • Client: Holds shared resources (HTTP client, token provider, hooks)
  • AccountClient: Bound to a specific account ID, provides service accessors

Examples:

Basic usage

config = Basecamp::Config.new(base_url: "https://3.basecampapi.com")
token = Basecamp::StaticTokenProvider.new(ENV["BASECAMP_TOKEN"])

client = Basecamp::Client.new(config: config, token_provider: token)
 = client.("12345")

# Use services (returns lazy Enumerator)
projects = .projects.list.to_a

With hooks for logging

class MyHooks
  include Basecamp::Hooks

  def on_request_start(info)
    puts "Starting #{info.method} #{info.url}"
  end

  def on_request_end(info, result)
    puts "Completed in #{result.duration}s"
  end
end

client = Basecamp::Client.new(config: config, token_provider: token, hooks: MyHooks.new)

Defined Under Namespace

Modules: AuthStrategy, ErrorCode, ExitCode, Hooks, Oauth, Security, Services, TokenProvider, Types, Webhooks Classes: AccountClient, AmbiguousError, ApiError, AuthError, BearerAuth, ChainHooks, Client, Config, DownloadResult, Error, ForbiddenError, Http, LoggerHooks, NetworkError, NoopHooks, NotFoundError, OauthTokenProvider, OperationInfo, OperationResult, RateLimitError, RequestInfo, RequestResult, Response, StaticTokenProvider, UsageError, ValidationError

Constant Summary collapse

VERSION =
"0.9.0"
API_VERSION =
"2026-07-24"

Class Method Summary collapse

Class Method Details

.client(access_token: nil, auth: nil, account_id: nil, base_url: Config::DEFAULT_BASE_URL, hooks: nil) ⇒ Client, AccountClient

Creates a new Basecamp client.

This is a convenience method that creates a Client with the given options.

Examples:

With access token

client = Basecamp.client(access_token: "abc123", account_id: "12345")
projects = client.projects.list.to_a

With custom auth strategy

client = Basecamp.client(auth: MyCustomAuth.new, account_id: "12345")

Parameters:

  • access_token (String, nil) (defaults to: nil)

    OAuth access token

  • auth (AuthStrategy, nil) (defaults to: nil)

    custom authentication strategy

  • account_id (String, nil) (defaults to: nil)

    Basecamp account ID (optional)

  • base_url (String) (defaults to: Config::DEFAULT_BASE_URL)

    Base URL for API requests

  • hooks (Hooks, nil) (defaults to: nil)

    Observability hooks

Returns:

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/basecamp.rb', line 70

def self.client(
  access_token: nil,
  auth: nil,
  account_id: nil,
  base_url: Config::DEFAULT_BASE_URL,
  hooks: nil
)
  raise ArgumentError, "provide either access_token or auth, not both" if access_token && auth
  raise ArgumentError, "provide access_token or auth" if !access_token && !auth

  config = Config.new(base_url: base_url)

  client = if auth
    Client.new(config: config, auth_strategy: auth, hooks: hooks)
  else
    token_provider = StaticTokenProvider.new(access_token)
    Client.new(config: config, token_provider: token_provider, hooks: hooks)
  end

   ? client.() : client
end

.error_from_response(status, body = nil, retry_after: nil) ⇒ Error

Maps an HTTP response to the appropriate error class.

Parameters:

  • status (Integer)

    HTTP status code

  • body (String, nil) (defaults to: nil)

    response body (will attempt JSON parse)

  • retry_after (Integer, nil) (defaults to: nil)

    Retry-After header value

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/basecamp.rb', line 98

def self.error_from_response(status, body = nil, retry_after: nil)
  message = parse_error_message(body) || "Request failed"

  case status
  when 400, 422
    ValidationError.new(message, http_status: status)
  when 401
    AuthError.new(message)
  when 403
    ForbiddenError.new(message)
  when 404
    NotFoundError.new(message: message)
  when 429
    RateLimitError.new(retry_after: retry_after)
  when 500
    ApiError.new("Server error (500)", http_status: 500, retryable: true)
  when 502, 503, 504
    ApiError.new("Gateway error (#{status})", http_status: status, retryable: true)
  else
    ApiError.from_status(status, message)
  end
end

.filename_from_url(raw_url) ⇒ Object

Extracts a filename from the last path segment of a URL. Falls back to "download" if the URL is unparseable or has no path segments.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/basecamp.rb', line 123

def self.filename_from_url(raw_url)
  uri = URI.parse(raw_url)
  path = uri.path
  return "download" if path.nil? || path.empty? || path == "/" || path.end_with?("/")

  segments = path.split("/").reject(&:empty?)
  return "download" if segments.empty?

  last = segments.last
  return "download" if last.nil? || last.empty? || last == "." || last == "/"

  URI::RFC2396_PARSER.unescape(last)
rescue URI::InvalidURIError
  "download"
end

.parse_error_message(body) ⇒ String?

Parses error message from response body.

Parameters:

  • body (String, nil)

Returns:

  • (String, nil)


142
143
144
145
146
147
148
149
150
151
152
# File 'lib/basecamp.rb', line 142

def self.parse_error_message(body)
  return nil if body.nil? || body.empty?

  Security.check_body_size!(body, Security::MAX_ERROR_BODY_BYTES, "Error")

  data = JSON.parse(body)
  msg = data["error"] || data["message"]
  msg ? Security.truncate(msg) : nil
rescue JSON::ParserError, ApiError
  nil
end