wefunder 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -13
  3. data/lib/wefunder/client.rb +127 -12
  4. data/lib/wefunder/version.rb +1 -1
  5. data/lib/wefunder/webhooks.rb +5 -2
  6. data/lib/wefunder_generated/api/activity_api.rb +0 -92
  7. data/lib/wefunder_generated/api/explore_api.rb +24 -6
  8. data/lib/wefunder_generated/api/intents_api.rb +2 -2
  9. data/lib/wefunder_generated/api/investment_delta_api.rb +4 -4
  10. data/lib/wefunder_generated/api/investments_api.rb +2 -2
  11. data/lib/wefunder_generated/api/member_links_api.rb +287 -0
  12. data/lib/wefunder_generated/api/partner_invites_sessions_api.rb +374 -0
  13. data/lib/wefunder_generated/api/portfolio_api.rb +2 -2
  14. data/lib/wefunder_generated/api/referral_codes_api.rb +374 -0
  15. data/lib/wefunder_generated/api/syndicate_deals_api.rb +2 -140
  16. data/lib/wefunder_generated/api/syndicate_members_api.rb +216 -0
  17. data/lib/wefunder_generated/api/syndicate_statistics_api.rb +2 -2
  18. data/lib/wefunder_generated/api/users_api.rb +4 -4
  19. data/lib/wefunder_generated/models/audit_event_attributes.rb +1 -0
  20. data/lib/wefunder_generated/models/campaign.rb +1 -1
  21. data/lib/wefunder_generated/models/company_attributes.rb +88 -1
  22. data/lib/wefunder_generated/models/company_disclosures_attributes_ratios.rb +13 -2
  23. data/lib/wefunder_generated/models/create_intent_request.rb +2 -2
  24. data/lib/wefunder_generated/models/{webhook_subscription_with_secret2_envelope.rb → create_offering_investment_session_request.rb} +17 -10
  25. data/lib/wefunder_generated/models/{attribution_stats_envelope.rb → create_offering_member_link_request.rb} +10 -10
  26. data/lib/wefunder_generated/models/{create_partner_invite_request.rb → create_referral_code_request.rb} +58 -63
  27. data/lib/wefunder_generated/models/{attribution_stats_period.rb → create_syndicate_join_session_request.rb} +17 -19
  28. data/lib/wefunder_generated/models/{anonymized_attribution.rb → create_syndicate_join_session_request_join_session.rb} +81 -53
  29. data/lib/wefunder_generated/models/create_webhook_endpoint_request.rb +11 -1
  30. data/lib/wefunder_generated/models/fiscal_year_financials.rb +25 -1
  31. data/lib/wefunder_generated/models/intent_attributes.rb +1 -0
  32. data/lib/wefunder_generated/models/investment_delta_record.rb +4 -4
  33. data/lib/wefunder_generated/models/investment_session_attributes.rb +1 -0
  34. data/lib/wefunder_generated/models/investment_session_create_input.rb +13 -2
  35. data/lib/wefunder_generated/models/investment_session_envelope.rb +1 -1
  36. data/lib/wefunder_generated/models/invite_link_attributes.rb +47 -4
  37. data/lib/wefunder_generated/models/invite_link_attributes_events_inner.rb +12 -1
  38. data/lib/wefunder_generated/models/invite_link_envelope.rb +1 -1
  39. data/lib/wefunder_generated/models/{full_attribution_status.rb → member_link.rb} +34 -24
  40. data/lib/wefunder_generated/models/{marketing_partner.rb → member_link_attributes.rb} +60 -44
  41. data/lib/wefunder_generated/models/{attribution_me_envelope.rb → member_link_create_input.rb} +40 -10
  42. data/lib/wefunder_generated/models/{spv_with_meta_envelope.rb → member_link_envelope.rb} +5 -5
  43. data/lib/wefunder_generated/models/{spv_list_envelope.rb → member_link_list_envelope.rb} +5 -5
  44. data/lib/wefunder_generated/models/{full_attribution_investor.rb → member_link_list_envelope_meta.rb} +28 -29
  45. data/lib/wefunder_generated/models/offering_attributes.rb +34 -1
  46. data/lib/wefunder_generated/models/offering_list_envelope_meta.rb +57 -3
  47. data/lib/wefunder_generated/models/portfolio_company_ref.rb +16 -5
  48. data/lib/wefunder_generated/models/portfolio_position_attributes.rb +13 -2
  49. data/lib/wefunder_generated/models/portfolio_security_terms.rb +15 -2
  50. data/lib/wefunder_generated/models/{spv_status.rb → referral_code.rb} +4 -4
  51. data/lib/wefunder_generated/models/{webhook_subscription.rb → referral_code_attributes.rb} +51 -63
  52. data/lib/wefunder_generated/models/{anonymized_attribution_status.rb → referral_code_attributes_target.rb} +36 -27
  53. data/lib/wefunder_generated/models/{spv_status_envelope.rb → referral_code_envelope.rb} +5 -5
  54. data/lib/wefunder_generated/models/{spv_with_meta_envelope_meta.rb → referral_code_envelope_meta.rb} +3 -3
  55. data/lib/wefunder_generated/models/{partner_investor_list_envelope.rb → referral_code_list_envelope.rb} +5 -5
  56. data/lib/wefunder_generated/models/{attribution_campaign_summary.rb → referral_code_list_envelope_meta.rb} +36 -35
  57. data/lib/wefunder_generated/models/{spv_status_attributes.rb → referral_code_stats_company.rb} +70 -47
  58. data/lib/wefunder_generated/models/referral_code_stats_envelope.rb +223 -0
  59. data/lib/wefunder_generated/models/{partner_investor.rb → referral_code_stats_envelope_data.rb} +4 -5
  60. data/lib/wefunder_generated/models/{syndicate_deal_finalize_intent_envelope_meta.rb → referral_code_stats_envelope_data_attributes.rb} +56 -10
  61. data/lib/wefunder_generated/models/{webhook_endpoint_envelope_meta.rb → referral_code_stats_envelope_meta.rb} +32 -4
  62. data/lib/wefunder_generated/models/{full_attribution.rb → referral_code_utm.rb} +130 -56
  63. data/lib/wefunder_generated/models/{attribution_me.rb → referral_code_visits.rb} +23 -25
  64. data/lib/wefunder_generated/models/{attribution_campaign_access.rb → referral_cohort.rb} +29 -28
  65. data/lib/wefunder_generated/models/referral_cohort_investors.rb +224 -0
  66. data/lib/wefunder_generated/models/syndicate_attributes.rb +11 -1
  67. data/lib/wefunder_generated/models/{syndicate_deal_close_intent_envelope.rb → syndicate_deal_created_envelope.rb} +5 -5
  68. data/lib/wefunder_generated/models/{attribution_campaign_summary_list_envelope.rb → syndicate_deal_created_envelope_meta.rb} +11 -12
  69. data/lib/wefunder_generated/models/{attribution_user.rb → syndicate_deal_created_envelope_meta_target_company.rb} +5 -14
  70. data/lib/wefunder_generated/models/{marketing_partner_envelope.rb → syndicate_deal_readiness_envelope.rb} +4 -4
  71. data/lib/wefunder_generated/models/syndicate_deal_readiness_envelope_data.rb +267 -0
  72. data/lib/wefunder_generated/models/{attribution_partner.rb → syndicate_deal_readiness_envelope_data_attributes.rb} +27 -33
  73. data/lib/wefunder_generated/models/{webhook_delivery.rb → syndicate_deal_readiness_item.rb} +55 -61
  74. data/lib/wefunder_generated/models/syndicate_statistics_attributes.rb +2 -2
  75. data/lib/wefunder_generated/models/test_webhook_endpoint_request.rb +2 -2
  76. data/lib/wefunder_generated/models/{webhook_test_result_envelope.rb → update_referral_code_request.rb} +47 -10
  77. data/lib/wefunder_generated/models/user_attributes.rb +1 -0
  78. data/lib/wefunder_generated/models/webhook_endpoint_attributes.rb +23 -1
  79. data/lib/wefunder_generated/models/webhook_endpoint_envelope.rb +1 -1
  80. data/lib/wefunder_generated/models/webhook_endpoint_test_result_envelope.rb +1 -1
  81. data/lib/wefunder_generated.rb +38 -63
  82. metadata +40 -65
  83. data/lib/wefunder_generated/api/attribution_api.rb +0 -182
  84. data/lib/wefunder_generated/api/attribution_partners_api.rb +0 -392
  85. data/lib/wefunder_generated/api/attribution_webhooks_api.rb +0 -504
  86. data/lib/wefunder_generated/models/anonymized_attribution_attribution.rb +0 -330
  87. data/lib/wefunder_generated/models/attributed_investment_list_envelope.rb +0 -226
  88. data/lib/wefunder_generated/models/attributed_investment_list_envelope_data_inner.rb +0 -105
  89. data/lib/wefunder_generated/models/attributed_investment_list_envelope_meta.rb +0 -295
  90. data/lib/wefunder_generated/models/attribution_stats.rb +0 -266
  91. data/lib/wefunder_generated/models/attribution_stats_by_campaign_inner.rb +0 -232
  92. data/lib/wefunder_generated/models/attribution_stats_by_source_inner.rb +0 -232
  93. data/lib/wefunder_generated/models/attribution_stats_quality_breakdown.rb +0 -236
  94. data/lib/wefunder_generated/models/attribution_stats_totals.rb +0 -245
  95. data/lib/wefunder_generated/models/create_webhook_subscription_request.rb +0 -263
  96. data/lib/wefunder_generated/models/full_attribution_attribution.rb +0 -326
  97. data/lib/wefunder_generated/models/partner_investment_list_envelope.rb +0 -225
  98. data/lib/wefunder_generated/models/partner_investor_attributes.rb +0 -264
  99. data/lib/wefunder_generated/models/partner_invite.rb +0 -360
  100. data/lib/wefunder_generated/models/partner_invite_envelope.rb +0 -214
  101. data/lib/wefunder_generated/models/partner_invite_list_envelope.rb +0 -216
  102. data/lib/wefunder_generated/models/register_as_partner_request.rb +0 -232
  103. data/lib/wefunder_generated/models/syndicate_deal_close_intent_envelope_meta.rb +0 -214
  104. data/lib/wefunder_generated/models/syndicate_deal_finalize_intent_envelope.rb +0 -224
  105. data/lib/wefunder_generated/models/webhook_subscription_envelope.rb +0 -214
  106. data/lib/wefunder_generated/models/webhook_subscription_list_envelope.rb +0 -225
  107. data/lib/wefunder_generated/models/webhook_subscription_list_envelope_meta.rb +0 -225
  108. data/lib/wefunder_generated/models/webhook_subscription_with_recent_deliveries_envelope.rb +0 -214
  109. data/lib/wefunder_generated/models/webhook_subscription_with_recent_deliveries_envelope_data.rb +0 -322
  110. data/lib/wefunder_generated/models/webhook_subscription_with_secret2_envelope_data.rb +0 -320
  111. data/lib/wefunder_generated/models/webhook_subscription_with_secret_envelope.rb +0 -214
  112. data/lib/wefunder_generated/models/webhook_subscription_with_secret_envelope_data.rb +0 -320
  113. data/lib/wefunder_generated/models/webhook_test_result.rb +0 -233
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f82d3280df8bafaa7518ab69f7af96d3bdfb1a2e253b6b6fb22794f65d40c701
4
- data.tar.gz: ff14e7f509d6868ed74b89beefd709cc7b9f30d84a5fdb54a7490193babfa8e5
3
+ metadata.gz: dc048e16fefefd8990b541796babbd387abc54f4e02f64af2c742ae31e45b375
4
+ data.tar.gz: e4595cd74e1b0fc03d20cb0b564d68d28deaedf56b600cc4e0cdcc07978edb5e
5
5
  SHA512:
6
- metadata.gz: 669cdc32cae0a4218a827efdc5418d8060fc6667b6c7b7c1ecef0405c232eaa37935e32ec6970b233ccc8e43e4638d447fcc7fec2eb61e9d8e53d9732972a941
7
- data.tar.gz: 708b4b330bf19f2ab9af55369576ac2a30e7ca7a1a9a7d989a5ee353869f46ca0162c2f7271e801650c601d64a0a2d2b21d85a8eeb76c8798c840d06e2755eba
6
+ metadata.gz: ab861d9306c4247e365fa1eef24f50d90efdb80375a22a33ad6b1f6f89c0091566c2059bea111400ca9377bff1dcd5fc01d53427f28a4e1cfb82641aea16b343
7
+ data.tar.gz: '08489fc8cb90890feebdb951227e008693d97b498ab5b5791312fbf27a4cd09237cb215cce8ed7523eec6f0c7d3e73ccc4d8b54b63a0efa768e4fc555c1c6eca'
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # wefunder
2
2
 
3
- Official Ruby SDK for the [Wefunder API](https://docs.wefunder.com). **Beta** — the API is pre-launch
4
- and `0.x` releases may include breaking changes.
3
+ Official Ruby SDK for the [Wefunder API](https://docs.wefunder.com).
5
4
 
6
5
  - A `Wefunder::Client` on [Faraday](https://lostisland.github.io/faraday/) with a generated, typed
7
6
  layer for every public operation
@@ -94,6 +93,15 @@ Concurrent requests that hit a 401 at the same time share one refresh (the clien
94
93
  If several application instances can use the same OAuth connection, serialize refreshes for that
95
94
  connection yourself.
96
95
 
96
+ ## API version
97
+
98
+ The SDK sends `Wefunder-Version: 2026-10-15` on every request (`DEFAULT_API_VERSION`); pass
99
+ `api_version:` to pin an older one. 1.1.0 moved the default from the pre-launch `2025-01-15` alias:
100
+ `GET /activity` and the syndicate deal `close` / `finalize` operations (and the
101
+ `syndicates.close_deal` / `finalize_deal` intent actions) are not part of 2026-10-15, so their
102
+ generated methods are gone; the legacy Attribution and attribution-webhook operations, removed
103
+ from the API on 2026-10-14, left with them (`wf.attribution` is gone; use `wf.referral_codes`).
104
+
97
105
  ## Calling the API
98
106
 
99
107
  ```ruby
@@ -103,13 +111,22 @@ portfolio = wf.portfolio.get
103
111
  ```
104
112
 
105
113
  Namespaces: `users`, `offerings`, `investments`, `portfolio`, `campaigns`, `syndicates`, `intents`,
106
- `attribution`, `installations`, and `webhook_endpoints`. Query parameters are keyword arguments;
114
+ `installations`, `member_links`, `referral_codes`, `join_sessions`, `investment_sessions`, and
115
+ `webhook_endpoints`. Query parameters are keyword arguments;
107
116
  enum-typed ones take plain strings, and `Time` / `DateTime` / `Date` values are sent as ISO 8601.
108
117
 
109
118
  `wf.installations` lets your app act as a company or syndicate: `eligible_targets`, `create`,
110
119
  `mint_token`, `list`, `get`, `revoke`, and `install_or_mint_token`, which handles the API's 409
111
120
  `already_installed` answer by minting a token for the existing install with the same scopes.
112
121
 
122
+ `wf.member_links` mints your app's per-person links to one deal page and reports what each member did
123
+ (`list`, `all`, `create`, `get`, `cancel`; needs a company- or syndicate-owned token). `wf.referral_codes`
124
+ (beta) creates codes with your server key and reads per-company stats (`list`, `all`, `get`, `create`,
125
+ `update`, `stats`). `wf.join_sessions` (beta) mints hosted syndicate join sessions (`list`, `all`, `get`,
126
+ `create`; the API publishes no schema for these yet, so they come back as string-keyed Hashes), and
127
+ `wf.investment_sessions` (beta) puts a known investor straight into checkout for one offering (`list`,
128
+ `all`, `list_for_offering`, `all_for_offering`, `get`, `create`, `cancel`).
129
+
113
130
  `wf.investments` is the Investment Delta API. `list` without a cursor bootstraps; pass `updated_since:`
114
131
  or the `meta.next_cursor` you saved from your last page to receive only records that changed since then.
115
132
  `next_cursor` is always present, even on the final page, so persist it after every sync.
@@ -138,16 +155,20 @@ API failures raise `Wefunder::Error`:
138
155
 
139
156
  ```ruby
140
157
  begin
141
- wf.syndicates.get("syn_example")
158
+ wf.syndicates.get("syn_aB3xQ9k2vF8mNp1zT5wY7Qc4")
142
159
  rescue Wefunder::Error => e
143
160
  puts e.status, e.type, e.message, e.request_id, e.remediation
144
161
  end
145
162
  ```
146
163
 
147
- The SDK retries idempotent `GET` requests after transient network errors, `5xx` responses, and rate
148
- limits (honouring `X-RateLimit-Reset`). Write requests are not retried automatically, except once after
149
- a `401` has been recovered. Exhausted network failures raise `Wefunder::Error` with `status` 0 and
150
- `type` `"network_error"`, from namespaces and `request` alike.
164
+ Retries are bounded (`retry_options:` `max_retries`, default 2) and differ by failure: a `429` is
165
+ retried for any method after waiting out `X-RateLimit-Reset`; transient network errors and `5xx`
166
+ responses are retried only for idempotent methods (`GET`, `HEAD`, `OPTIONS`); a `401` is recovered
167
+ once, for any method, by refreshing or re-minting the token and resending. Size request timeouts with
168
+ the rate-limit wait in mind, since rate limiting alone can attempt a write up to three times, and the
169
+ one-time 401 recovery can add one more. Exhausted
170
+ network failures raise `Wefunder::Error` with `status` 0 and `type` `"network_error"`, from namespaces
171
+ and `request` alike.
151
172
 
152
173
  `timeout:` (seconds, default 30) applies to every request the client makes: API calls, `request`, and
153
174
  the OAuth token round-trips for refresh and re-mint.
@@ -223,7 +244,7 @@ generated under `WefunderGenerated::<Tag>Api` and reachable through `wf.raw.<tag
223
244
  the same auth, retries, and typed errors:
224
245
 
225
246
  ```ruby
226
- members = wf.wrap { wf.raw.syndicate_members.list_syndicate_members("syn_example") }
247
+ members = wf.wrap { wf.raw.syndicate_members.list_syndicate_members("syn_aB3xQ9k2vF8mNp1zT5wY7Qc4") }
227
248
  ```
228
249
 
229
250
  For a path the generated layer does not know yet, `wf.request(:get, path, query:, body:, headers:)`
@@ -269,12 +290,13 @@ Commit the specification and generated client together.
269
290
 
270
291
  ### Releasing
271
292
 
272
- Set the version in `lib/wefunder/version.rb` (e.g. `0.1.0.beta2`), rebuild `examples_manifest.json`,
273
- commit, then:
293
+ Set the version in `lib/wefunder/version.rb` (e.g. `1.1.0`; a prerelease is `1.2.0.beta1`), run
294
+ `bundle install` so `Gemfile.lock` follows, rebuild `examples_manifest.json`, commit, then:
274
295
 
275
296
  ```bash
276
- git tag v0.1.0.beta2
277
- git push --follow-tags
297
+ git push origin main
298
+ git tag v1.1.0
299
+ git push origin v1.1.0 # push the tag explicitly: `--follow-tags` only pushes annotated tags
278
300
  ```
279
301
 
280
302
  The release workflow verifies the tag, runs the checks, and publishes to RubyGems via trusted publishing.
@@ -9,7 +9,7 @@ module Wefunder
9
9
  # Version-free base — the edge gateway serves the API at the host root; +/api/v2+ remains a
10
10
  # back-compat alias. The API version is pinned via the +Wefunder-Version+ header, not the path.
11
11
  DEFAULT_API_BASE_URL = "https://api.wefunder.com"
12
- DEFAULT_API_VERSION = "2025-01-15"
12
+ DEFAULT_API_VERSION = "2026-10-15"
13
13
  API_VERSION_HEADER = "Wefunder-Version"
14
14
 
15
15
  # "live" / "test" from the access-token prefix. UX only — never used for routing.
@@ -26,7 +26,8 @@ module Wefunder
26
26
  # typed-error unwrapping, and lazy auto-pagination. +raw+ exposes every generated API.
27
27
  class Client
28
28
  attr_reader :mode, :users, :offerings, :investments, :portfolio, :campaigns, :syndicates, :intents,
29
- :attribution, :installations, :webhook_endpoints
29
+ :installations, :member_links, :referral_codes, :join_sessions, :investment_sessions,
30
+ :webhook_endpoints
30
31
 
31
32
  def initialize(access_token: nil, tokens: nil, client_id: nil, client_secret: nil, client_credentials: nil,
32
33
  api_version: DEFAULT_API_VERSION, base_url: DEFAULT_API_BASE_URL, authorize_base_url: nil,
@@ -52,8 +53,11 @@ module Wefunder
52
53
  @campaigns = Campaigns.new(self)
53
54
  @syndicates = Syndicates.new(self)
54
55
  @intents = Intents.new(self)
55
- @attribution = Attribution.new(self)
56
56
  @installations = Installations.new(self)
57
+ @member_links = MemberLinks.new(self)
58
+ @referral_codes = ReferralCodes.new(self)
59
+ @join_sessions = JoinSessions.new(self)
60
+ @investment_sessions = InvestmentSessions.new(self)
57
61
  @webhook_endpoints = WebhookEndpoints.new(self)
58
62
  end
59
63
 
@@ -166,13 +170,14 @@ module Wefunder
166
170
  # Lazily-built generated API instances, one per tag.
167
171
  class Raw
168
172
  APIS = {
169
- activity: "ActivityApi", attribution: "AttributionApi", attribution_partners: "AttributionPartnersApi",
170
- attribution_webhooks: "AttributionWebhooksApi", campaigns: "CampaignsApi", companies: "CompaniesApi",
171
- explore: "ExploreApi", installations: "InstallationsApi", intents: "IntentsApi",
172
- investment_delta: "InvestmentDeltaApi", investments: "InvestmentsApi", portfolio: "PortfolioApi",
173
- syndicate_deals: "SyndicateDealsApi", syndicate_members: "SyndicateMembersApi",
174
- syndicate_portfolio: "SyndicatePortfolioApi", syndicate_statistics: "SyndicateStatisticsApi",
175
- syndicates: "SyndicatesApi", users: "UsersApi", webhook_endpoints: "WebhookEndpointsApi"
173
+ activity: "ActivityApi", campaigns: "CampaignsApi", companies: "CompaniesApi", explore: "ExploreApi",
174
+ installations: "InstallationsApi", intents: "IntentsApi", investment_delta: "InvestmentDeltaApi",
175
+ investments: "InvestmentsApi", member_links: "MemberLinksApi",
176
+ partner_invites_sessions: "PartnerInvitesSessionsApi", portfolio: "PortfolioApi",
177
+ referral_codes: "ReferralCodesApi", syndicate_deals: "SyndicateDealsApi",
178
+ syndicate_members: "SyndicateMembersApi", syndicate_portfolio: "SyndicatePortfolioApi",
179
+ syndicate_statistics: "SyndicateStatisticsApi", syndicates: "SyndicatesApi", users: "UsersApi",
180
+ webhook_endpoints: "WebhookEndpointsApi"
176
181
  }.freeze
177
182
 
178
183
  def initialize(api_client)
@@ -272,8 +277,118 @@ module Wefunder
272
277
  def preview(body) = data_of(wrap { raw.intents.preview_intent(WefunderGenerated::PreviewIntentRequest.new(body)) })
273
278
  end
274
279
 
275
- class Attribution < Namespace
276
- def me = data_of(wrap { raw.attribution_partners.get_attribution_me })
280
+ # /syndicates/{id}/join_sessions (write:syndicate_members to mint, read:syndicates to read; beta).
281
+ # Mint a hosted session for an email, send the person to +attributes["url"]+, and learn the
282
+ # outcome from +get+ or the +join_session.*+ webhooks. The spec publishes no response schema for
283
+ # these yet, so sessions come back as plain Hashes.
284
+ class JoinSessions < Namespace
285
+ def list(syndicate_id, **opts)
286
+ strings(wrap { raw.syndicate_members.list_syndicate_join_sessions(syndicate_id, norm(opts)) })
287
+ end
288
+
289
+ def all(syndicate_id, **opts)
290
+ sessions = pages(opts) { |o| raw.syndicate_members.list_syndicate_join_sessions(syndicate_id, o) }
291
+ Enumerator.new { |y| sessions.each { |session| y << strings(session) } }
292
+ end
293
+
294
+ def get(syndicate_id, session_id)
295
+ strings(data_of(wrap { raw.syndicate_members.get_syndicate_join_session(syndicate_id, session_id) }))
296
+ end
297
+
298
+ # Mint a session for one person (+email:+ required). Needs a manager's user token or the syndicate's own.
299
+ def create(syndicate_id, **attrs)
300
+ body = WefunderGenerated::CreateSyndicateJoinSessionRequest.new(
301
+ join_session: WefunderGenerated::CreateSyndicateJoinSessionRequestJoinSession.new(attrs)
302
+ )
303
+ strings(data_of(wrap { raw.syndicate_members.create_syndicate_join_session(syndicate_id, body) }))
304
+ end
305
+
306
+ private
307
+
308
+ # The generated client parses an untyped +object+ response with symbol keys; hand it back the
309
+ # way the documented examples read it (+session["attributes"]["url"]+).
310
+ def strings(obj) = JSON.parse(JSON.generate(obj))
311
+ end
312
+
313
+ # /offerings/{id}/investment_sessions and /investment_sessions (read:investment_sessions /
314
+ # write:investment_sessions; beta). A session puts a known investor straight into checkout for one
315
+ # offering and tracks that one investment.
316
+ class InvestmentSessions < Namespace
317
+ # Every session your app created, across offerings; the envelope.
318
+ def list(**opts) = wrap { raw.partner_invites_sessions.list_investment_sessions(norm(opts)) }
319
+ def all(**opts) = pages(opts) { |o| raw.partner_invites_sessions.list_investment_sessions(o) }
320
+
321
+ def list_for_offering(offering_id, **opts)
322
+ wrap { raw.partner_invites_sessions.list_offering_investment_sessions(offering_id, norm(opts)) }
323
+ end
324
+
325
+ def all_for_offering(offering_id, **opts)
326
+ pages(opts) { |o| raw.partner_invites_sessions.list_offering_investment_sessions(offering_id, o) }
327
+ end
328
+
329
+ def get(session_id) = data_of(wrap { raw.partner_invites_sessions.get_investment_session(session_id) })
330
+
331
+ # Create a session on an offering; +idempotency_key:+ makes a retry safe.
332
+ def create(offering_id, idempotency_key: nil, **attrs)
333
+ body = WefunderGenerated::CreateOfferingInvestmentSessionRequest.new(
334
+ investment_session: WefunderGenerated::InvestmentSessionCreateInput.new(attrs)
335
+ )
336
+ opts = idempotency_key ? { idempotency_key: idempotency_key } : {}
337
+ data_of(wrap { raw.partner_invites_sessions.create_offering_investment_session(offering_id, body, opts) })
338
+ end
339
+
340
+ # Cancel a session the recipient has not completed.
341
+ def cancel(session_id) = data_of(wrap { raw.partner_invites_sessions.cancel_investment_session(session_id) })
342
+ end
343
+
344
+ # /offerings/{id}/member_links and /member_links/{id} (read:invite_links / write:invite_links):
345
+ # your app's per-person links that send a member to one deal page and report what they did.
346
+ # Needs a company- or syndicate-owned token of an app installed on the offering's syndicate or
347
+ # company; a user token gets 404.
348
+ class MemberLinks < Namespace
349
+ # One page of the offering's links; the envelope.
350
+ def list(offering_id, **opts) = wrap { raw.member_links.list_offering_member_links(offering_id, norm(opts)) }
351
+ def all(offering_id) = pages({}) { |o| raw.member_links.list_offering_member_links(offering_id, o) }
352
+
353
+ # Mint a link (+reference:+, optional +wefunder_user_id:+). Repeating a +reference+ returns the
354
+ # live link instead of a second one; +idempotency_key:+ makes a retried request safe as well.
355
+ def create(offering_id, idempotency_key: nil, **attrs)
356
+ opts = {}
357
+ opts[:idempotency_key] = idempotency_key if idempotency_key
358
+ unless attrs.empty?
359
+ opts[:create_offering_member_link_request] =
360
+ WefunderGenerated::CreateOfferingMemberLinkRequest.new(member_link: WefunderGenerated::MemberLinkCreateInput.new(attrs))
361
+ end
362
+ data_of(wrap { raw.member_links.create_offering_member_link(offering_id, opts) })
363
+ end
364
+
365
+ def get(link_id) = data_of(wrap { raw.member_links.get_member_link(link_id) })
366
+ # Stop an unused link from opening the deal page.
367
+ def cancel(link_id) = data_of(wrap { raw.member_links.cancel_member_link(link_id) })
368
+ end
369
+
370
+ # /referral_codes (read:referral_codes / write:referral_codes; beta). Mint with your app's server
371
+ # key, add +?wfr=ref_…+ to any wefunder.com link, and read +stats+. What +stats+ shows per company
372
+ # depends on its +level+: +counts+ is weekly cohorts (+weeks+), +anonymized+ is per-investment rows
373
+ # with opaque tokens (+investments+), and +full+, for an app installed on the company with a
374
+ # founder-tier grant, carries investor names and emails.
375
+ class ReferralCodes < Namespace
376
+ def list(**opts) = wrap { raw.referral_codes.list_referral_codes(norm(opts)) }
377
+ def all(**opts) = pages(opts) { |o| raw.referral_codes.list_referral_codes(o) }
378
+ def get(code_id) = data_of(wrap { raw.referral_codes.get_referral_code(code_id) })
379
+
380
+ # Not idempotent: a retried create can mint a second code, which you can deactivate.
381
+ def create(**attrs)
382
+ data_of(wrap { raw.referral_codes.create_referral_code(WefunderGenerated::CreateReferralCodeRequest.new(attrs)) })
383
+ end
384
+
385
+ def update(code_id, **changes)
386
+ body = WefunderGenerated::UpdateReferralCodeRequest.new(changes)
387
+ data_of(wrap { raw.referral_codes.update_referral_code(code_id, body) })
388
+ end
389
+
390
+ # Weekly cohorts per company; the envelope (+meta.next_cursor+ pages the company list).
391
+ def stats(code_id, **opts) = wrap { raw.referral_codes.get_referral_code_stats(code_id, norm(opts)) }
277
392
  end
278
393
 
279
394
  # /installations (read:installations / write:installations; write does not imply read). An
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wefunder
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -36,12 +36,15 @@ module Wefunder
36
36
  # spec/webhooks_spec.rb, which fails when the two diverge.
37
37
  EVENT_NAMES = %w[
38
38
  investment.created investment.reinstated investment.canceled investment.converted
39
- investment.amount_changed investment.executed investment.changed
39
+ investment.amount_changed investment.executed investment.changed investment.referred
40
40
  offering.opened offering.closing offering.closed offering.canceled
41
41
  investment_session.created investment_session.started investment_session.completed
42
42
  investment_session.expired investment_session.canceled
43
43
  syndicate_member.invited syndicate_member.reinvited syndicate_member.applied
44
- syndicate_member.approved syndicate_member.joined
44
+ syndicate_member.approved syndicate_member.joined syndicate_member.left syndicate_member.declined
45
+ join_session.pending_approval join_session.completed join_session.rejected join_session.ineligible
46
+ join_session.canceled join_session.expired
47
+ installation.created installation.revoked referral_cohorts.published
45
48
  ].freeze
46
49
 
47
50
  # Raised by +construct_event+. Respond 400 and don't process.
@@ -19,98 +19,6 @@ module WefunderGenerated
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
- # List activity events
23
- # Returns actions taken by OAuth apps authorized by the current user. This is the API backing the user-facing activity feed at Settings > Activity.
24
- # @param [Hash] opts the optional parameters
25
- # @option opts [Integer] :app_id Filter by connected app
26
- # @option opts [String] :action_pattern Filter by action pattern (e.g. syndicates.* or syndicates.member.*)
27
- # @option opts [String] :resource_type Filter by resource type (e.g. Club). Must be used with resource_id.
28
- # @option opts [String] :resource_id Filter by specific resource ID. Must be used with resource_type. For Club resources, pass the syndicate&#39;s id (&#x60;syn_...&#x60;).
29
- # @option opts [String] :status
30
- # @option opts [Time] :since
31
- # @option opts [Time] :_until
32
- # @option opts [String] :cursor Opaque cursor from the previous response&#39;s &#x60;meta.next_cursor&#x60;.
33
- # @option opts [Integer] :per_page (default to 25)
34
- # @return [AuditEventListEnvelope]
35
- def list_activity(opts = {})
36
- data, _status_code, _headers = list_activity_with_http_info(opts)
37
- data
38
- end
39
-
40
- # List activity events
41
- # Returns actions taken by OAuth apps authorized by the current user. This is the API backing the user-facing activity feed at Settings &gt; Activity.
42
- # @param [Hash] opts the optional parameters
43
- # @option opts [Integer] :app_id Filter by connected app
44
- # @option opts [String] :action_pattern Filter by action pattern (e.g. syndicates.* or syndicates.member.*)
45
- # @option opts [String] :resource_type Filter by resource type (e.g. Club). Must be used with resource_id.
46
- # @option opts [String] :resource_id Filter by specific resource ID. Must be used with resource_type. For Club resources, pass the syndicate&#39;s id (&#x60;syn_...&#x60;).
47
- # @option opts [String] :status
48
- # @option opts [Time] :since
49
- # @option opts [Time] :_until
50
- # @option opts [String] :cursor Opaque cursor from the previous response&#39;s &#x60;meta.next_cursor&#x60;.
51
- # @option opts [Integer] :per_page (default to 25)
52
- # @return [Array<(AuditEventListEnvelope, Integer, Hash)>] AuditEventListEnvelope data, response status code and response headers
53
- def list_activity_with_http_info(opts = {})
54
- if @api_client.config.debugging
55
- @api_client.config.logger.debug 'Calling API: ActivityApi.list_activity ...'
56
- end
57
- allowable_values = ["success", "failure", "denied"]
58
- if @api_client.config.client_side_validation && opts[:'status'] && !allowable_values.include?(opts[:'status'])
59
- fail ArgumentError, "invalid value for \"status\", must be one of #{allowable_values}"
60
- end
61
- if @api_client.config.client_side_validation && !opts[:'per_page'].nil? && opts[:'per_page'] > 100
62
- fail ArgumentError, 'invalid value for "opts[:"per_page"]" when calling ActivityApi.list_activity, must be smaller than or equal to 100.'
63
- end
64
-
65
- # resource path
66
- local_var_path = '/activity'
67
-
68
- # query parameters
69
- query_params = opts[:query_params] || {}
70
- query_params[:'app_id'] = opts[:'app_id'] if !opts[:'app_id'].nil?
71
- query_params[:'action_pattern'] = opts[:'action_pattern'] if !opts[:'action_pattern'].nil?
72
- query_params[:'resource_type'] = opts[:'resource_type'] if !opts[:'resource_type'].nil?
73
- query_params[:'resource_id'] = opts[:'resource_id'] if !opts[:'resource_id'].nil?
74
- query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
75
- query_params[:'since'] = opts[:'since'] if !opts[:'since'].nil?
76
- query_params[:'until'] = opts[:'_until'] if !opts[:'_until'].nil?
77
- query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil?
78
- query_params[:'per_page'] = opts[:'per_page'] if !opts[:'per_page'].nil?
79
-
80
- # header parameters
81
- header_params = opts[:header_params] || {}
82
- # HTTP header 'Accept' (if needed)
83
- header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
84
-
85
- # form parameters
86
- form_params = opts[:form_params] || {}
87
-
88
- # http body (model)
89
- post_body = opts[:debug_body]
90
-
91
- # return_type
92
- return_type = opts[:debug_return_type] || 'AuditEventListEnvelope'
93
-
94
- # auth_names
95
- auth_names = opts[:debug_auth_names] || ['bearerAuth', 'bearerAuth']
96
-
97
- new_options = opts.merge(
98
- :operation => :"ActivityApi.list_activity",
99
- :header_params => header_params,
100
- :query_params => query_params,
101
- :form_params => form_params,
102
- :body => post_body,
103
- :auth_names => auth_names,
104
- :return_type => return_type
105
- )
106
-
107
- data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
108
- if @api_client.config.debugging
109
- @api_client.config.logger.debug "API called: ActivityApi#list_activity\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
110
- end
111
- return data, status_code, headers
112
- end
113
-
114
22
  # List connected apps
115
23
  # Returns all OAuth apps the user has authorized, with summary stats.
116
24
  # @param [Hash] opts the optional parameters
@@ -20,7 +20,7 @@ module WefunderGenerated
20
20
  @api_client = api_client
21
21
  end
22
22
  # Get a company page
23
- # The company page behind an offering, as structured data — the \"click a company\" step after `/explore`. Addressed by the `co_...` id every offering carries in `company.id`. Keeps apart the three raised numbers the page shows and readers conflate: `current_raise.amount_raised` (this campaign's live rounds combined, on Wefunder), `past_rounds[]` (prior rounds the page lists, each tagged `source: wefunder` for rounds observed here or `reported` for founder-disclosed off-platform rounds), and `totals.profile_total_raised` (the number on the page's ticker bar). Every figure comes from the same policy the page uses, so the API never disagrees with the site. **Who has a page.** Every company whose profile wefunder.com lists publicly — the same set the site's search bar returns (published, not invite-only, searchable or with a live Form C; accredited-only companies need an accredited viewer) — plus the `/explore` set. A funded company, or one the viewer may see no round of, renders with `raising: false` and `current_raise: null`; its history is in `wefunder_rounds`. **Logged-in view (`read:explore`)** additionally covers the vault / syndicate-led companies the site shows an accredited viewer, and the round described is the one the page would show that viewer. A valid id for a company outside the viewer's set is a `404`, never a partial payload.
23
+ # The company page behind an offering, as structured data — the \"click a company\" step after `/explore`. Addressed by the `co_...` id every offering carries in its top-level `company` field (a bare string). Keeps apart the three raised numbers the page shows and readers conflate: `current_raise.amount_raised` (this campaign's live rounds combined, on Wefunder), `past_rounds[]` (prior rounds the page lists, each tagged `source: wefunder` for rounds observed here or `reported` for founder-disclosed off-platform rounds), and `totals.profile_total_raised` (the number on the page's ticker bar). Every figure comes from the same policy the page uses, so the API never disagrees with the site. **Who has a page.** Every company whose profile wefunder.com lists publicly — the same set the site's search bar returns (published, not invite-only, searchable or with a live Form C; accredited-only companies need an accredited viewer) — plus the `/explore` set. A funded company, or one the viewer may see no round of, renders with `raising: false` and `current_raise: null`; its history is in `wefunder_rounds`. **Logged-in view (`read:explore`)** additionally covers the vault / syndicate-led companies the site shows an accredited viewer, and the round described is the one the page would show that viewer. A valid id for a company outside the viewer's set is a `404`, never a partial payload.
24
24
  # @param id [String] The company&#39;s id (&#x60;co_...&#x60;).
25
25
  # @param [Hash] opts the optional parameters
26
26
  # @return [CompanyEnvelope]
@@ -30,7 +30,7 @@ module WefunderGenerated
30
30
  end
31
31
 
32
32
  # Get a company page
33
- # The company page behind an offering, as structured data — the \&quot;click a company\&quot; step after &#x60;/explore&#x60;. Addressed by the &#x60;co_...&#x60; id every offering carries in &#x60;company.id&#x60;. Keeps apart the three raised numbers the page shows and readers conflate: &#x60;current_raise.amount_raised&#x60; (this campaign&#39;s live rounds combined, on Wefunder), &#x60;past_rounds[]&#x60; (prior rounds the page lists, each tagged &#x60;source: wefunder&#x60; for rounds observed here or &#x60;reported&#x60; for founder-disclosed off-platform rounds), and &#x60;totals.profile_total_raised&#x60; (the number on the page&#39;s ticker bar). Every figure comes from the same policy the page uses, so the API never disagrees with the site. **Who has a page.** Every company whose profile wefunder.com lists publicly — the same set the site&#39;s search bar returns (published, not invite-only, searchable or with a live Form C; accredited-only companies need an accredited viewer) — plus the &#x60;/explore&#x60; set. A funded company, or one the viewer may see no round of, renders with &#x60;raising: false&#x60; and &#x60;current_raise: null&#x60;; its history is in &#x60;wefunder_rounds&#x60;. **Logged-in view (&#x60;read:explore&#x60;)** additionally covers the vault / syndicate-led companies the site shows an accredited viewer, and the round described is the one the page would show that viewer. A valid id for a company outside the viewer&#39;s set is a &#x60;404&#x60;, never a partial payload.
33
+ # The company page behind an offering, as structured data — the \&quot;click a company\&quot; step after &#x60;/explore&#x60;. Addressed by the &#x60;co_...&#x60; id every offering carries in its top-level &#x60;company&#x60; field (a bare string). Keeps apart the three raised numbers the page shows and readers conflate: &#x60;current_raise.amount_raised&#x60; (this campaign&#39;s live rounds combined, on Wefunder), &#x60;past_rounds[]&#x60; (prior rounds the page lists, each tagged &#x60;source: wefunder&#x60; for rounds observed here or &#x60;reported&#x60; for founder-disclosed off-platform rounds), and &#x60;totals.profile_total_raised&#x60; (the number on the page&#39;s ticker bar). Every figure comes from the same policy the page uses, so the API never disagrees with the site. **Who has a page.** Every company whose profile wefunder.com lists publicly — the same set the site&#39;s search bar returns (published, not invite-only, searchable or with a live Form C; accredited-only companies need an accredited viewer) — plus the &#x60;/explore&#x60; set. A funded company, or one the viewer may see no round of, renders with &#x60;raising: false&#x60; and &#x60;current_raise: null&#x60;; its history is in &#x60;wefunder_rounds&#x60;. **Logged-in view (&#x60;read:explore&#x60;)** additionally covers the vault / syndicate-led companies the site shows an accredited viewer, and the round described is the one the page would show that viewer. A valid id for a company outside the viewer&#39;s set is a &#x60;404&#x60;, never a partial payload.
34
34
  # @param id [String] The company&#39;s id (&#x60;co_...&#x60;).
35
35
  # @param [Hash] opts the optional parameters
36
36
  # @return [Array<(CompanyEnvelope, Integer, Hash)>] CompanyEnvelope data, response status code and response headers
@@ -521,10 +521,12 @@ module WefunderGenerated
521
521
  end
522
522
 
523
523
  # List public offerings
524
- # Returns the offerings shown on wefunder.com/explore — the curated set of companies that meet Wefunder's discoverability bar (a soft-confirmed traction threshold plus approval), **one offering per company**. An \"offering\" is a fundraise, addressed by its id (`ofr_...`). **Public view (`read:public`).** Reachable with either a server-side (client_credentials) token or a user access token. Private rounds (e.g. Reg D 506(b)) are never listed, and low-traction or unapproved companies are excluded — exactly as they are on the website for a logged-out visitor. **Logged-in view (`read:explore`).** A user access token carrying `read:explore` gets what *that user* sees on wefunder.com/explore: the public offerings plus any the user qualifies for (accredited-only / Vault deals, invited private rounds), each checked against the same visibility policy the website applies. The response is a strict superset of the public one — same fields and ids — with two extra attributes, `publicly_visible` and `invested`, and `meta.personalized: true`. A server-side key can never hold `read:explore`; a token with `read:explore` but no resolvable user gets the public view. Order with `sort` (default: most raised); paginate with `cursor`. `meta.total_count` is the number of offerings across all pages, after filters. **Filters** are objective, per-offering terms — exemption family, security type, TTW vs. live, minimum investment ceiling, close date, amount raised floor. The response echoes the applied set in `meta.filters`. An out-of-vocabulary value is a `400` naming the accepted values. There are deliberately no relevance, quality, or ranking criteria. Use this endpoint to: - Discover and monitor active deals - Build deal aggregators and public listings
524
+ # Returns the offerings shown on wefunder.com/explore — the curated set of companies that meet Wefunder's discoverability bar (a soft-confirmed traction threshold plus approval), **one offering per company**. An \"offering\" is a fundraise, addressed by its id (`ofr_...`). **Public view (`read:public`).** Reachable with either a server-side (client_credentials) token or a user access token. Private rounds (e.g. Reg D 506(b)) are never listed, and low-traction or unapproved companies are excluded — exactly as they are on the website for a logged-out visitor. **Logged-in view (`read:explore`).** A user access token carrying `read:explore` gets what *that user* sees on wefunder.com/explore: the public offerings plus any the user qualifies for (accredited-only / Vault deals, invited private rounds), each checked against the same visibility policy the website applies. The response is a strict superset of the public one — same fields and ids — with two extra attributes, `publicly_visible` and `invested`, and `meta.personalized: true`. A server-side key can never hold `read:explore`; a token with `read:explore` but no resolvable user gets the public view. Order with `sort` (default: most raised); paginate with `cursor` and `per_page`. `meta.total_count` is the number of offerings across all pages, after filters. **Closed rounds (`status`).** By default the list is live rounds. `status=closed` lists rounds that finished raising instead, newest `closed_at` first. It is not limited to one per company or to the curated explore set: it covers every finished round that `GET /offerings/{id}` serves to a logged-out visitor, for companies listed in Wefunder's public directory. `status=all` returns the live list first, then the closed list. With `status=closed` or `status=all`, `exemption` is the only filter accepted, and `sort` orders only the live part. Closed rounds follow the public view even for a `read:explore` token. **Filters** are objective, per-offering terms — exemption family, security type, TTW vs. live, minimum investment ceiling, close date, amount raised floor. The response echoes the applied set in `meta.filters`. An out-of-vocabulary value is a `400` naming the accepted values. There are deliberately no relevance, quality, or ranking criteria. Use this endpoint to: - Discover and monitor active deals - Build deal aggregators and public listings - Bulk-list closed rounds (`status=closed`)
525
525
  # @param [Hash] opts the optional parameters
526
526
  # @option opts [Integer] :cursor Cursor-based pagination token. Use the &#x60;next_cursor&#x60; value from the previous response&#39;s &#x60;meta&#x60; object to retrieve the next page of results. Omit this parameter to retrieve the first page.
527
- # @option opts [String] :sort Result ordering: - &#x60;most_raised&#x60; (default) — highest amount raised first - &#x60;closing_soon&#x60; — soonest funding close first - &#x60;newest&#x60; — most recently launched first - &#x60;most_investors&#x60; — highest investor count first (default to 'most_raised')
527
+ # @option opts [Integer] :per_page Offerings per page. (default to 25)
528
+ # @option opts [String] :status Which rounds to list: - &#x60;live&#x60; (default): rounds accepting investments or reservations, as on wefunder.com/explore - &#x60;closed&#x60;: rounds that finished raising, newest &#x60;closed_at&#x60; first (id breaks ties) - &#x60;all&#x60;: the live list, then the closed list Any other value is a &#x60;400&#x60;. (default to 'live')
529
+ # @option opts [String] :sort Ordering of live rounds. A &#x60;400&#x60; with &#x60;status&#x3D;closed&#x60;; with &#x60;status&#x3D;all&#x60; it orders the live part only. - &#x60;most_raised&#x60; (default) — highest amount raised first - &#x60;closing_soon&#x60; — soonest funding close first - &#x60;newest&#x60; — most recently launched first - &#x60;most_investors&#x60; — highest investor count first (default to 'most_raised')
528
530
  # @option opts [String] :exemption Only offerings under this SEC exemption family. Reg CF and Reg D are the supported public filters.
529
531
  # @option opts [String] :security Only offerings issuing this kind of security — the &#x60;security.type&#x60; value on results.
530
532
  # @option opts [Boolean] :testing_the_waters &#x60;false&#x60; — only live rounds accepting investments; &#x60;true&#x60; — only Testing-the-Waters rounds collecting non-binding reservations.
@@ -541,10 +543,12 @@ module WefunderGenerated
541
543
  end
542
544
 
543
545
  # List public offerings
544
- # Returns the offerings shown on wefunder.com/explore — the curated set of companies that meet Wefunder&#39;s discoverability bar (a soft-confirmed traction threshold plus approval), **one offering per company**. An \&quot;offering\&quot; is a fundraise, addressed by its id (&#x60;ofr_...&#x60;). **Public view (&#x60;read:public&#x60;).** Reachable with either a server-side (client_credentials) token or a user access token. Private rounds (e.g. Reg D 506(b)) are never listed, and low-traction or unapproved companies are excluded — exactly as they are on the website for a logged-out visitor. **Logged-in view (&#x60;read:explore&#x60;).** A user access token carrying &#x60;read:explore&#x60; gets what *that user* sees on wefunder.com/explore: the public offerings plus any the user qualifies for (accredited-only / Vault deals, invited private rounds), each checked against the same visibility policy the website applies. The response is a strict superset of the public one — same fields and ids — with two extra attributes, &#x60;publicly_visible&#x60; and &#x60;invested&#x60;, and &#x60;meta.personalized: true&#x60;. A server-side key can never hold &#x60;read:explore&#x60;; a token with &#x60;read:explore&#x60; but no resolvable user gets the public view. Order with &#x60;sort&#x60; (default: most raised); paginate with &#x60;cursor&#x60;. &#x60;meta.total_count&#x60; is the number of offerings across all pages, after filters. **Filters** are objective, per-offering terms — exemption family, security type, TTW vs. live, minimum investment ceiling, close date, amount raised floor. The response echoes the applied set in &#x60;meta.filters&#x60;. An out-of-vocabulary value is a &#x60;400&#x60; naming the accepted values. There are deliberately no relevance, quality, or ranking criteria. Use this endpoint to: - Discover and monitor active deals - Build deal aggregators and public listings
546
+ # Returns the offerings shown on wefunder.com/explore — the curated set of companies that meet Wefunder&#39;s discoverability bar (a soft-confirmed traction threshold plus approval), **one offering per company**. An \&quot;offering\&quot; is a fundraise, addressed by its id (&#x60;ofr_...&#x60;). **Public view (&#x60;read:public&#x60;).** Reachable with either a server-side (client_credentials) token or a user access token. Private rounds (e.g. Reg D 506(b)) are never listed, and low-traction or unapproved companies are excluded — exactly as they are on the website for a logged-out visitor. **Logged-in view (&#x60;read:explore&#x60;).** A user access token carrying &#x60;read:explore&#x60; gets what *that user* sees on wefunder.com/explore: the public offerings plus any the user qualifies for (accredited-only / Vault deals, invited private rounds), each checked against the same visibility policy the website applies. The response is a strict superset of the public one — same fields and ids — with two extra attributes, &#x60;publicly_visible&#x60; and &#x60;invested&#x60;, and &#x60;meta.personalized: true&#x60;. A server-side key can never hold &#x60;read:explore&#x60;; a token with &#x60;read:explore&#x60; but no resolvable user gets the public view. Order with &#x60;sort&#x60; (default: most raised); paginate with &#x60;cursor&#x60; and &#x60;per_page&#x60;. &#x60;meta.total_count&#x60; is the number of offerings across all pages, after filters. **Closed rounds (&#x60;status&#x60;).** By default the list is live rounds. &#x60;status&#x3D;closed&#x60; lists rounds that finished raising instead, newest &#x60;closed_at&#x60; first. It is not limited to one per company or to the curated explore set: it covers every finished round that &#x60;GET /offerings/{id}&#x60; serves to a logged-out visitor, for companies listed in Wefunder&#39;s public directory. &#x60;status&#x3D;all&#x60; returns the live list first, then the closed list. With &#x60;status&#x3D;closed&#x60; or &#x60;status&#x3D;all&#x60;, &#x60;exemption&#x60; is the only filter accepted, and &#x60;sort&#x60; orders only the live part. Closed rounds follow the public view even for a &#x60;read:explore&#x60; token. **Filters** are objective, per-offering terms — exemption family, security type, TTW vs. live, minimum investment ceiling, close date, amount raised floor. The response echoes the applied set in &#x60;meta.filters&#x60;. An out-of-vocabulary value is a &#x60;400&#x60; naming the accepted values. There are deliberately no relevance, quality, or ranking criteria. Use this endpoint to: - Discover and monitor active deals - Build deal aggregators and public listings - Bulk-list closed rounds (&#x60;status&#x3D;closed&#x60;)
545
547
  # @param [Hash] opts the optional parameters
546
548
  # @option opts [Integer] :cursor Cursor-based pagination token. Use the &#x60;next_cursor&#x60; value from the previous response&#39;s &#x60;meta&#x60; object to retrieve the next page of results. Omit this parameter to retrieve the first page.
547
- # @option opts [String] :sort Result ordering: - &#x60;most_raised&#x60; (default) — highest amount raised first - &#x60;closing_soon&#x60; — soonest funding close first - &#x60;newest&#x60; — most recently launched first - &#x60;most_investors&#x60; — highest investor count first (default to 'most_raised')
549
+ # @option opts [Integer] :per_page Offerings per page. (default to 25)
550
+ # @option opts [String] :status Which rounds to list: - &#x60;live&#x60; (default): rounds accepting investments or reservations, as on wefunder.com/explore - &#x60;closed&#x60;: rounds that finished raising, newest &#x60;closed_at&#x60; first (id breaks ties) - &#x60;all&#x60;: the live list, then the closed list Any other value is a &#x60;400&#x60;. (default to 'live')
551
+ # @option opts [String] :sort Ordering of live rounds. A &#x60;400&#x60; with &#x60;status&#x3D;closed&#x60;; with &#x60;status&#x3D;all&#x60; it orders the live part only. - &#x60;most_raised&#x60; (default) — highest amount raised first - &#x60;closing_soon&#x60; — soonest funding close first - &#x60;newest&#x60; — most recently launched first - &#x60;most_investors&#x60; — highest investor count first (default to 'most_raised')
548
552
  # @option opts [String] :exemption Only offerings under this SEC exemption family. Reg CF and Reg D are the supported public filters.
549
553
  # @option opts [String] :security Only offerings issuing this kind of security — the &#x60;security.type&#x60; value on results.
550
554
  # @option opts [Boolean] :testing_the_waters &#x60;false&#x60; — only live rounds accepting investments; &#x60;true&#x60; — only Testing-the-Waters rounds collecting non-binding reservations.
@@ -559,6 +563,18 @@ module WefunderGenerated
559
563
  if @api_client.config.debugging
560
564
  @api_client.config.logger.debug 'Calling API: ExploreApi.list_offerings ...'
561
565
  end
566
+ if @api_client.config.client_side_validation && !opts[:'per_page'].nil? && opts[:'per_page'] > 100
567
+ fail ArgumentError, 'invalid value for "opts[:"per_page"]" when calling ExploreApi.list_offerings, must be smaller than or equal to 100.'
568
+ end
569
+
570
+ if @api_client.config.client_side_validation && !opts[:'per_page'].nil? && opts[:'per_page'] < 1
571
+ fail ArgumentError, 'invalid value for "opts[:"per_page"]" when calling ExploreApi.list_offerings, must be greater than or equal to 1.'
572
+ end
573
+
574
+ allowable_values = ["live", "closed", "all"]
575
+ if @api_client.config.client_side_validation && opts[:'status'] && !allowable_values.include?(opts[:'status'])
576
+ fail ArgumentError, "invalid value for \"status\", must be one of #{allowable_values}"
577
+ end
562
578
  allowable_values = ["most_raised", "closing_soon", "newest", "most_investors"]
563
579
  if @api_client.config.client_side_validation && opts[:'sort'] && !allowable_values.include?(opts[:'sort'])
564
580
  fail ArgumentError, "invalid value for \"sort\", must be one of #{allowable_values}"
@@ -585,6 +601,8 @@ module WefunderGenerated
585
601
  # query parameters
586
602
  query_params = opts[:query_params] || {}
587
603
  query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil?
604
+ query_params[:'per_page'] = opts[:'per_page'] if !opts[:'per_page'].nil?
605
+ query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
588
606
  query_params[:'sort'] = opts[:'sort'] if !opts[:'sort'].nil?
589
607
  query_params[:'exemption'] = opts[:'exemption'] if !opts[:'exemption'].nil?
590
608
  query_params[:'security'] = opts[:'security'] if !opts[:'security'].nil?
@@ -20,7 +20,7 @@ module WefunderGenerated
20
20
  @api_client = api_client
21
21
  end
22
22
  # Propose an intent
23
- # Propose a dangerous or irreversible action for human approval. Returns a `review_url` where a human must review the impact and approve before the action executes. The entity that proposes an intent can never approve it. Approval always happens on Wefunder's UI by a user with permission on the resource. Each action names the scope that may propose it and the resource type it targets (`syndicates.*` → `write:syndicates` on a syndicate; `comments.create` → `write:comments` on a `Company`). The token must hold that action's scope; holding another proposing scope is not enough (403 `insufficient_scope`). `comments.create` posts a question on a company's Ask tab, or an answer to one, as the requesting user once they approve on wefunder.com. `resource_id` is the company (`co_...`); `params` is `{ target_type: \"company\" | \"comment\", target: \"co_...\" | \"cmt_...\", body, disclosure_key? }` (`disclosure_key` ∈ investor, stockholder, promoter, financial_stakeholder). Who may post is the site's own rule: anyone the page is available to may ask (unless the company has closed questions); only the company's team may answer. Only the requester can approve. 20 proposals per user per day (429). An `idempotency_key` names one operation. Reusing it for the same operation returns the existing intent (200) while that intent is pending, approved, executing, or executed; reusing it for a different action, resource, or params is a 409 `idempotency_conflict`. An action still rolling out per account returns 403 `feature_disabled` for a user who is not yet enabled. `POST /intents/preview` runs the same checks without minting. See [Intents documentation](/concepts/intents) for the full pattern.
23
+ # Propose a dangerous or irreversible action for human approval. Returns a `review_url` where a human must review the impact and approve before the action executes. The entity that proposes an intent can never approve it. Approval always happens on Wefunder's UI by a user with permission on the resource. Each action names the scope that may propose it and the resource type it targets (`syndicates.*` → `write:syndicates` on a syndicate; `comments.create` → `write:comments` on a `Company`). The token must hold that action's scope; holding another proposing scope is not enough (403 `insufficient_scope`). `comments.create` posts a question on a company's Ask tab, or an answer to one, as the requesting user once they approve on wefunder.com. `resource_id` is the company (`co_...`); `params` is `{ target_type: \"company\" | \"comment\", target: \"co_...\" | \"cmt_...\", body, disclosure_key? }` (`disclosure_key` ∈ investor, stockholder, promoter, financial_stakeholder). Who may post is the site's own rule: anyone the page is available to may ask (unless the company has closed questions); only the company's team may answer. Only the requester can approve. 20 proposals per user per day (429). An `idempotency_key` names one operation. Reusing it for the same operation returns the existing intent (200) while that intent is pending, approved, executing, or executed; reusing it for a different action, resource, or params is a 409 `idempotency_conflict`. An action still rolling out per account returns 403 `feature_disabled` for a user who is not yet enabled. `POST /intents/preview` runs the same checks without minting. `syndicates.close_deal` and `syndicates.finalize_deal` were removed in `2026-10-15`: on this version they answer 400 `bad_request` (\"Unknown action\") with `details.removed_in_version`. A syndicate's managers close and finalize deals on wefunder.com. Intents minted before still appear in `GET /intents` with those action names. See [Intents documentation](/concepts/intents) for the full pattern.
24
24
  # @param create_intent_request [CreateIntentRequest]
25
25
  # @param [Hash] opts the optional parameters
26
26
  # @return [IntentEnvelope]
@@ -30,7 +30,7 @@ module WefunderGenerated
30
30
  end
31
31
 
32
32
  # Propose an intent
33
- # Propose a dangerous or irreversible action for human approval. Returns a &#x60;review_url&#x60; where a human must review the impact and approve before the action executes. The entity that proposes an intent can never approve it. Approval always happens on Wefunder&#39;s UI by a user with permission on the resource. Each action names the scope that may propose it and the resource type it targets (&#x60;syndicates.*&#x60; → &#x60;write:syndicates&#x60; on a syndicate; &#x60;comments.create&#x60; → &#x60;write:comments&#x60; on a &#x60;Company&#x60;). The token must hold that action&#39;s scope; holding another proposing scope is not enough (403 &#x60;insufficient_scope&#x60;). &#x60;comments.create&#x60; posts a question on a company&#39;s Ask tab, or an answer to one, as the requesting user once they approve on wefunder.com. &#x60;resource_id&#x60; is the company (&#x60;co_...&#x60;); &#x60;params&#x60; is &#x60;{ target_type: \&quot;company\&quot; | \&quot;comment\&quot;, target: \&quot;co_...\&quot; | \&quot;cmt_...\&quot;, body, disclosure_key? }&#x60; (&#x60;disclosure_key&#x60; ∈ investor, stockholder, promoter, financial_stakeholder). Who may post is the site&#39;s own rule: anyone the page is available to may ask (unless the company has closed questions); only the company&#39;s team may answer. Only the requester can approve. 20 proposals per user per day (429). An &#x60;idempotency_key&#x60; names one operation. Reusing it for the same operation returns the existing intent (200) while that intent is pending, approved, executing, or executed; reusing it for a different action, resource, or params is a 409 &#x60;idempotency_conflict&#x60;. An action still rolling out per account returns 403 &#x60;feature_disabled&#x60; for a user who is not yet enabled. &#x60;POST /intents/preview&#x60; runs the same checks without minting. See [Intents documentation](/concepts/intents) for the full pattern.
33
+ # Propose a dangerous or irreversible action for human approval. Returns a &#x60;review_url&#x60; where a human must review the impact and approve before the action executes. The entity that proposes an intent can never approve it. Approval always happens on Wefunder&#39;s UI by a user with permission on the resource. Each action names the scope that may propose it and the resource type it targets (&#x60;syndicates.*&#x60; → &#x60;write:syndicates&#x60; on a syndicate; &#x60;comments.create&#x60; → &#x60;write:comments&#x60; on a &#x60;Company&#x60;). The token must hold that action&#39;s scope; holding another proposing scope is not enough (403 &#x60;insufficient_scope&#x60;). &#x60;comments.create&#x60; posts a question on a company&#39;s Ask tab, or an answer to one, as the requesting user once they approve on wefunder.com. &#x60;resource_id&#x60; is the company (&#x60;co_...&#x60;); &#x60;params&#x60; is &#x60;{ target_type: \&quot;company\&quot; | \&quot;comment\&quot;, target: \&quot;co_...\&quot; | \&quot;cmt_...\&quot;, body, disclosure_key? }&#x60; (&#x60;disclosure_key&#x60; ∈ investor, stockholder, promoter, financial_stakeholder). Who may post is the site&#39;s own rule: anyone the page is available to may ask (unless the company has closed questions); only the company&#39;s team may answer. Only the requester can approve. 20 proposals per user per day (429). An &#x60;idempotency_key&#x60; names one operation. Reusing it for the same operation returns the existing intent (200) while that intent is pending, approved, executing, or executed; reusing it for a different action, resource, or params is a 409 &#x60;idempotency_conflict&#x60;. An action still rolling out per account returns 403 &#x60;feature_disabled&#x60; for a user who is not yet enabled. &#x60;POST /intents/preview&#x60; runs the same checks without minting. &#x60;syndicates.close_deal&#x60; and &#x60;syndicates.finalize_deal&#x60; were removed in &#x60;2026-10-15&#x60;: on this version they answer 400 &#x60;bad_request&#x60; (\&quot;Unknown action\&quot;) with &#x60;details.removed_in_version&#x60;. A syndicate&#39;s managers close and finalize deals on wefunder.com. Intents minted before still appear in &#x60;GET /intents&#x60; with those action names. See [Intents documentation](/concepts/intents) for the full pattern.
34
34
  # @param create_intent_request [CreateIntentRequest]
35
35
  # @param [Hash] opts the optional parameters
36
36
  # @return [Array<(IntentEnvelope, Integer, Hash)>] IntentEnvelope data, response status code and response headers
@@ -19,8 +19,8 @@ module WefunderGenerated
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
- # Bootstrap or page investment changes for a company
23
- # The Investment Delta API: keep a copy of a company's investment records in sync by polling. Call without `cursor` to **bootstrap** (list every current record for the company); every response carries `meta.next_cursor`; call again with that cursor to receive only the records whose published state changed since. Records are the same shape in both modes. - Records are **published state**, not live rows: Wefunder publishes a record when the investment's founder-visible state changes, and serves exactly what was published. - `visible: false` records are **tombstones**: the investment is no longer visible to the company (canceled, converted to another round, hidden, or deleted). Delete your copy. - Apply records in `cursor` order. `observed_at` is when the payload was observed, not a version; a record you read late carries the *current* published state. - **410 Gone** means your cursor predates the 90-day retention window. Bootstrap again and **replace** your dataset for the company from the result (records absent from the bootstrap are deleted). - Investor identity fields (`investor.name`, `investor.legal_name`, `investor.email`, `investor.address`, `investor.bio`, `message`, `external_username`) are **omitted** unless the token carries `read:investors:pii`. - Money is integer minor units plus `amounts.currency` (`committed_cents`, `investment_size_cents`, `in_escrow_cents`, `contracts[].override_amount_cents`). `shares` and `average_share_price` are decimal strings, `null` on rounds without shares. `amounts.raised_cents` is the row's contribution to the public raised figure (0 while `needs_whitelisting`); sum it, not `committed_cents`, to match the deal page.
22
+ # Legacy alias: bootstrap or page investment changes for a company
23
+ # **Compatibility alias.** This path keeps working unchanged for partners already on it, but new integrations should use [List or sync investments](/api-reference/list-investments) (`GET /investments?company_id=…`): same records, interchangeable cursors. The [migration notes](/partner-api/investments/changes#migrating-from-companiescompany_idinvestmentschanges) list the differences. The Investment Delta API: keep a copy of a company's investment records in sync by polling. Call without `cursor` to **bootstrap** (list every current record for the company); every response carries `meta.next_cursor`; call again with that cursor to receive only the records whose published state changed since. Records are the same shape in both modes. - Records are **published state**, not live rows: Wefunder publishes a record when the investment's founder-visible state changes, and serves exactly what was published. - `visible: false` records are **tombstones**: the investment is no longer visible to the company (canceled, converted to another round, hidden, or deleted). Delete your copy. - Apply records in `cursor` order. `observed_at` is when the payload was observed, not a version; a record you read late carries the *current* published state. - **410 Gone** means your cursor predates the 90-day retention window. Bootstrap again and **replace** your dataset for the company from the result (records absent from the bootstrap are deleted). - Investor identity fields (`investor.name`, `investor.legal_name`, `investor.email`, `investor.address`, `investor.bio`, `message`, `external_username`) are **omitted** unless the token carries `read:investors:pii`. - Money is integer minor units plus `amounts.currency` (`committed_cents`, `investment_size_cents`, `in_escrow_cents`, `contracts[].override_amount_cents`). `shares` and `average_share_price` are decimal strings, `null` on rounds without shares. `amounts.raised_cents` is the row's contribution to the public raised figure (0 while `needs_whitelisting`); sum it, not `committed_cents`, to match the deal page.
24
24
  # @param company_id [String] The company&#39;s external id (&#x60;co_…&#x60;).
25
25
  # @param [Hash] opts the optional parameters
26
26
  # @option opts [String] :cursor Opaque cursor from a previous response&#39;s &#x60;meta.next_cursor&#x60;. Omit to bootstrap.
@@ -31,8 +31,8 @@ module WefunderGenerated
31
31
  data
32
32
  end
33
33
 
34
- # Bootstrap or page investment changes for a company
35
- # The Investment Delta API: keep a copy of a company&#39;s investment records in sync by polling. Call without &#x60;cursor&#x60; to **bootstrap** (list every current record for the company); every response carries &#x60;meta.next_cursor&#x60;; call again with that cursor to receive only the records whose published state changed since. Records are the same shape in both modes. - Records are **published state**, not live rows: Wefunder publishes a record when the investment&#39;s founder-visible state changes, and serves exactly what was published. - &#x60;visible: false&#x60; records are **tombstones**: the investment is no longer visible to the company (canceled, converted to another round, hidden, or deleted). Delete your copy. - Apply records in &#x60;cursor&#x60; order. &#x60;observed_at&#x60; is when the payload was observed, not a version; a record you read late carries the *current* published state. - **410 Gone** means your cursor predates the 90-day retention window. Bootstrap again and **replace** your dataset for the company from the result (records absent from the bootstrap are deleted). - Investor identity fields (&#x60;investor.name&#x60;, &#x60;investor.legal_name&#x60;, &#x60;investor.email&#x60;, &#x60;investor.address&#x60;, &#x60;investor.bio&#x60;, &#x60;message&#x60;, &#x60;external_username&#x60;) are **omitted** unless the token carries &#x60;read:investors:pii&#x60;. - Money is integer minor units plus &#x60;amounts.currency&#x60; (&#x60;committed_cents&#x60;, &#x60;investment_size_cents&#x60;, &#x60;in_escrow_cents&#x60;, &#x60;contracts[].override_amount_cents&#x60;). &#x60;shares&#x60; and &#x60;average_share_price&#x60; are decimal strings, &#x60;null&#x60; on rounds without shares. &#x60;amounts.raised_cents&#x60; is the row&#39;s contribution to the public raised figure (0 while &#x60;needs_whitelisting&#x60;); sum it, not &#x60;committed_cents&#x60;, to match the deal page.
34
+ # Legacy alias: bootstrap or page investment changes for a company
35
+ # **Compatibility alias.** This path keeps working unchanged for partners already on it, but new integrations should use [List or sync investments](/api-reference/list-investments) (&#x60;GET /investments?company_id&#x3D;…&#x60;): same records, interchangeable cursors. The [migration notes](/partner-api/investments/changes#migrating-from-companiescompany_idinvestmentschanges) list the differences. The Investment Delta API: keep a copy of a company&#39;s investment records in sync by polling. Call without &#x60;cursor&#x60; to **bootstrap** (list every current record for the company); every response carries &#x60;meta.next_cursor&#x60;; call again with that cursor to receive only the records whose published state changed since. Records are the same shape in both modes. - Records are **published state**, not live rows: Wefunder publishes a record when the investment&#39;s founder-visible state changes, and serves exactly what was published. - &#x60;visible: false&#x60; records are **tombstones**: the investment is no longer visible to the company (canceled, converted to another round, hidden, or deleted). Delete your copy. - Apply records in &#x60;cursor&#x60; order. &#x60;observed_at&#x60; is when the payload was observed, not a version; a record you read late carries the *current* published state. - **410 Gone** means your cursor predates the 90-day retention window. Bootstrap again and **replace** your dataset for the company from the result (records absent from the bootstrap are deleted). - Investor identity fields (&#x60;investor.name&#x60;, &#x60;investor.legal_name&#x60;, &#x60;investor.email&#x60;, &#x60;investor.address&#x60;, &#x60;investor.bio&#x60;, &#x60;message&#x60;, &#x60;external_username&#x60;) are **omitted** unless the token carries &#x60;read:investors:pii&#x60;. - Money is integer minor units plus &#x60;amounts.currency&#x60; (&#x60;committed_cents&#x60;, &#x60;investment_size_cents&#x60;, &#x60;in_escrow_cents&#x60;, &#x60;contracts[].override_amount_cents&#x60;). &#x60;shares&#x60; and &#x60;average_share_price&#x60; are decimal strings, &#x60;null&#x60; on rounds without shares. &#x60;amounts.raised_cents&#x60; is the row&#39;s contribution to the public raised figure (0 while &#x60;needs_whitelisting&#x60;); sum it, not &#x60;committed_cents&#x60;, to match the deal page.
36
36
  # @param company_id [String] The company&#39;s external id (&#x60;co_…&#x60;).
37
37
  # @param [Hash] opts the optional parameters
38
38
  # @option opts [String] :cursor Opaque cursor from a previous response&#39;s &#x60;meta.next_cursor&#x60;. Omit to bootstrap.