wefunder 1.0.1 → 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 +33 -11
  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: 22ebe2c46ab040572b224cc28c95b23f67171daecb4bc52c8b8ff452813028ed
4
- data.tar.gz: 60214039ed4ed0f69de281a4d65ff773090e650c632ae14c068e7f7dcb78a26c
3
+ metadata.gz: dc048e16fefefd8990b541796babbd387abc54f4e02f64af2c742ae31e45b375
4
+ data.tar.gz: e4595cd74e1b0fc03d20cb0b564d68d28deaedf56b600cc4e0cdcc07978edb5e
5
5
  SHA512:
6
- metadata.gz: 3c8e4dea352276f65485cc9a38339005cea7c33ab971be5f9db7f67cd4e9daeeb8e00b83f2649a25601018a1e016fc9014fcd088c0f00e919b0fe64cb6753e34
7
- data.tar.gz: a22b19c1f5e7df31eba45530dbf453149f92b5e339f0323b4b4b014e2916fa31d7fade5da738708ac04569b318c6cbe69d5c30a142642ec1c6ca328e6fb88c0b
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.
@@ -144,10 +161,14 @@ rescue Wefunder::Error => e
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.
@@ -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.1"
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.
@@ -146,7 +146,7 @@ module WefunderGenerated
146
146
  end
147
147
 
148
148
  # List or sync investments
149
- # The investment records the token may see, served from **published** state (docs: *Investments: list, sync, retrieve*). The audience is the union of the companies the token's user edits (their founder-visible records) and the user's own investments (every applied record of theirs, whatever its status). Filters narrow within that audience; they never widen it. Three ways to call it: - **List** (no `cursor`, no `updated_since`): the current records, tombstones excluded, ordered by investment. Page with `meta.next_cursor` while `meta.has_more`; the final page's cursor is a sync cursor positioned at the moment the listing started. - **Sync by cursor**: only the records whose published state changed since the cursor, one record per investment, tombstones (`visible: false`, only `id`) included. Apply in `cursor` order; always store `meta.next_cursor`. - **Sync by time**: `updated_since=<ISO 8601>` is sugar for a cursor at the ledger position just before that time (with a 60 s safety margin). The response is a normal sync page, so continue with its `meta.next_cursor`. Records are eventually consistent (about ten minutes on the common paths); each carries `observed_at`. `GET /investments/{id}` returns the same shape derived now. Investor identity fields on other investors' records require `read:investors:pii`; a user always sees their own. Money is integer minor units plus `amounts.currency`. **410 Gone** means the cursor or `updated_since` predates the 90-day retention window: list again and replace your dataset. `status` cannot be combined with sync (a record leaving the status would never be delivered).
149
+ # The investment records the token may see, served from **published** state (docs: *Investments: list, sync, retrieve*). The audience is the union of the companies the token's user edits (their founder-visible records) and the user's own investments (every applied record of theirs, whatever its status). Filters narrow within that audience; they never widen it. Three ways to call it: - **List** (no `cursor`, no `updated_since`): the current records, tombstones excluded, ordered by investment. Page with `meta.next_cursor` while `meta.has_more`; the final page's cursor is a sync cursor positioned at the moment the listing started. - **Sync by cursor**: only the records whose published state changed since the cursor, one record per investment, tombstones included (`visible: false` with `id`, `observed_at`, `cursor` and `reason`, and no payload fields). Apply in `cursor` order; always store `meta.next_cursor`. Filters are not encoded in the cursor: repeat the same `company_id`, `offering_id` or `investor_id` on every sync request, or the sync widens to the whole audience. - **Sync by time**: `updated_since=<ISO 8601>` is sugar for a cursor at the ledger position just before that time (with a 60 s safety margin). The response is a normal sync page, so continue with its `meta.next_cursor`. Records are eventually consistent (about ten minutes on the common paths); each carries `observed_at`. `GET /investments/{id}` returns the same shape derived now. Investor identity fields on other investors' records require `read:investors:pii`; a user always sees their own. Money is integer minor units plus `amounts.currency`. **410 Gone** means the cursor or `updated_since` predates the 90-day retention window: list again and replace your dataset. `status` cannot be combined with sync (a record leaving the status would never be delivered).
150
150
  # @param [Hash] opts the optional parameters
151
151
  # @option opts [String] :company_id Company external id (&#x60;co_…&#x60;).
152
152
  # @option opts [String] :offering_id Offering external id (&#x60;ofr_…&#x60;). In sync mode an investment that moved to another offering is delivered once more as a tombstone under this filter.
@@ -162,7 +162,7 @@ module WefunderGenerated
162
162
  end
163
163
 
164
164
  # List or sync investments
165
- # The investment records the token may see, served from **published** state (docs: *Investments: list, sync, retrieve*). The audience is the union of the companies the token&#39;s user edits (their founder-visible records) and the user&#39;s own investments (every applied record of theirs, whatever its status). Filters narrow within that audience; they never widen it. Three ways to call it: - **List** (no &#x60;cursor&#x60;, no &#x60;updated_since&#x60;): the current records, tombstones excluded, ordered by investment. Page with &#x60;meta.next_cursor&#x60; while &#x60;meta.has_more&#x60;; the final page&#39;s cursor is a sync cursor positioned at the moment the listing started. - **Sync by cursor**: only the records whose published state changed since the cursor, one record per investment, tombstones (&#x60;visible: false&#x60;, only &#x60;id&#x60;) included. Apply in &#x60;cursor&#x60; order; always store &#x60;meta.next_cursor&#x60;. - **Sync by time**: &#x60;updated_since&#x3D;&lt;ISO 8601&gt;&#x60; is sugar for a cursor at the ledger position just before that time (with a 60 s safety margin). The response is a normal sync page, so continue with its &#x60;meta.next_cursor&#x60;. Records are eventually consistent (about ten minutes on the common paths); each carries &#x60;observed_at&#x60;. &#x60;GET /investments/{id}&#x60; returns the same shape derived now. Investor identity fields on other investors&#39; records require &#x60;read:investors:pii&#x60;; a user always sees their own. Money is integer minor units plus &#x60;amounts.currency&#x60;. **410 Gone** means the cursor or &#x60;updated_since&#x60; predates the 90-day retention window: list again and replace your dataset. &#x60;status&#x60; cannot be combined with sync (a record leaving the status would never be delivered).
165
+ # The investment records the token may see, served from **published** state (docs: *Investments: list, sync, retrieve*). The audience is the union of the companies the token&#39;s user edits (their founder-visible records) and the user&#39;s own investments (every applied record of theirs, whatever its status). Filters narrow within that audience; they never widen it. Three ways to call it: - **List** (no &#x60;cursor&#x60;, no &#x60;updated_since&#x60;): the current records, tombstones excluded, ordered by investment. Page with &#x60;meta.next_cursor&#x60; while &#x60;meta.has_more&#x60;; the final page&#39;s cursor is a sync cursor positioned at the moment the listing started. - **Sync by cursor**: only the records whose published state changed since the cursor, one record per investment, tombstones included (&#x60;visible: false&#x60; with &#x60;id&#x60;, &#x60;observed_at&#x60;, &#x60;cursor&#x60; and &#x60;reason&#x60;, and no payload fields). Apply in &#x60;cursor&#x60; order; always store &#x60;meta.next_cursor&#x60;. Filters are not encoded in the cursor: repeat the same &#x60;company_id&#x60;, &#x60;offering_id&#x60; or &#x60;investor_id&#x60; on every sync request, or the sync widens to the whole audience. - **Sync by time**: &#x60;updated_since&#x3D;&lt;ISO 8601&gt;&#x60; is sugar for a cursor at the ledger position just before that time (with a 60 s safety margin). The response is a normal sync page, so continue with its &#x60;meta.next_cursor&#x60;. Records are eventually consistent (about ten minutes on the common paths); each carries &#x60;observed_at&#x60;. &#x60;GET /investments/{id}&#x60; returns the same shape derived now. Investor identity fields on other investors&#39; records require &#x60;read:investors:pii&#x60;; a user always sees their own. Money is integer minor units plus &#x60;amounts.currency&#x60;. **410 Gone** means the cursor or &#x60;updated_since&#x60; predates the 90-day retention window: list again and replace your dataset. &#x60;status&#x60; cannot be combined with sync (a record leaving the status would never be delivered).
166
166
  # @param [Hash] opts the optional parameters
167
167
  # @option opts [String] :company_id Company external id (&#x60;co_…&#x60;).
168
168
  # @option opts [String] :offering_id Offering external id (&#x60;ofr_…&#x60;). In sync mode an investment that moved to another offering is delivered once more as a tombstone under this filter.