OpportunitiesEntities
Docs
    Ring Platform

    Decentralized Self-building Future

    Sign In
    Entities
    Opportunities
    Store
    Docs
    Platform Concepts
    RING EconomySonoratek LLCGlobal ImpactAI Meets Web3
    Get Started
    Quick StartCalculatorRoadmap
    Privacy|Contact
    v1.106.26|Sonoratek LLC

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Ring Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

    Opportunities API

    REST for the dual-nature marketplace. The browse list is open to visitors; everything else needs a session. Browse list (GET /api/opportunities) is role-aware cursor pagination plus per-viewer enrichment. Search is a separate route. Like / Save / Hide / Contact are Server Actions, not HTTP resources.

    Use Founder / Developer tabs to filter this page. Product loop and card UX: Opportunities.

    Not seven types

    Older API copy listed a fixed “7 types.” Runtime types are the OpportunityType union in features/opportunities/types/index.ts, gated by ring-config.json → opportunities.enabledTypes. Do not document a closed set of seven.

    Surfaces (shared)

    MethodPathAuthNotes
    GET/api/opportunitiesOptionallimit, startAfter. Filter params types/type, q/query, categories/category, location, priority switch to the search service. Passes viewerUserId when signed in. Returns { opportunities, items, lastVisible, cursor, hasMore }
    POST/api/opportunitiesSessionBody createOpportunityBodySchema → { opportunity } 201
    GET/api/opportunities/{id}SessionSingle listing
    PUT / PATCH/api/opportunities/{id}SessionUpdate
    DELETE/api/opportunities/{id}SessionDelete
    GET/api/opportunities/searchSessionRequires q. Optional types, categories, location, limit
    POST/api/opportunities/searchSessionJSON body SearchOpportunitiesParams
    POST/api/opportunities/{id}/signSessionAdvisory petition signature, one per user. Returns signatureCount, signatureTarget
    GET/api/opportunities/mySessionOwner list; view= all | drafts | pending | active | archived
    POST/api/opportunities/uploadSessionAttachments
    GET / POST/api/mcp/v1/opportunitiesMCP guardList does not pass viewerUserId today

    Since 2026-10-17, unauthenticated GET /api/opportunities returns public, non-confidential rows for role visitor. The visitor filter runs after the page is fetched, so a guest page can hold fewer than limit rows. Detail, search and mutations still return 401 without a session.

    What operators need to know

    • Browse vs search. The /opportunities page loads GET /api/opportunities (a page of listings). Right-rail filters are sent as query params and applied on the server. Visitors can browse without an account.
    • Hidden listings. When a member taps Hide, the next browse/search for that account omits the id. Other members still see it.
    • Names on cards. List and search responses attach a public-safe creator snapshot so the UI never prints a raw UUID.
    • Create in product. The add-opportunity form still prefers the createOpportunity Server Action (app/_actions/opportunities.ts). REST POST is for API clients and MCP; both call the same service.

    Feature loop

    Matcher, enabled types, feed card actions, My Opportunities.

    Discovery sync

    Cache tags, revalidatePath, Tunnel opportunity:* after create/update/delete.

    List enrichment

    GET /api/opportunities (app/api/opportunities/route.ts):

    1. auth(). No session means role visitor, no viewer enrichment.
    2. With filter params: searchOpportunities. Otherwise getOpportunitiesForRole({ userRole, limit, startAfter, viewerUserId }).
    3. If viewerUserId is set: getViewerHiddenOpportunityIds then DatabaseService filter { field: 'id', operator: 'not-in', value: hiddenIds }.
    4. attachOpportunityFeedFields adds creator, viewer, likes.

    searchOpportunities reads session.user.id (or MCP actor id) internally — callers do not pass viewerUserId. Hidden exclusion and attach run when that id exists.

    MCP GET /api/mcp/v1/opportunities calls getOpportunitiesForRole without viewerUserId (no hide filter, no creator/viewer on that list).

    SerializedOpportunity extras (list/search)

    Canonical type file: features/opportunities/types/index.ts. Dates are ISO strings. Firestore Timestamp shapes exist only for DB_BACKEND_MODE=firebase-full.

    Create body (required)

    createOpportunityBodySchema (features/opportunities/lib/create-opportunity-schema.ts): title, briefDescription, organizationId, category, location, visibility, contactInfo.linkedEntity, contactInfo.contactAccount.

    POST returns 201 { opportunity }. Create then calls syncOpportunityDiscovery with a snippet that includes creator { id, name, avatar } from the session (live-inserted cards).

    Interactions (not REST)

    Access control

    • Visibility on the document: public | subscriber | member | confidential | entity. entity rows are readable only by admins, the poster and members of the linked entity. List queries apply role-aware visibility filters (buildOpportunityVisibilityFilters).
    • Confidential listings require confidential-capable roles.
    • Apply is in-app (no dedicated /apply REST route in alpha).

    Frequently asked questions

    Impact

    Can I list opportunities without a session?

    Yes, public non-confidential rows only. GET /api/opportunities/{id} and search still require a session.

    Does search honor Hide?

    Yes when searchOpportunities has a session (or MCP actor) user id. The HTTP GET/POST search routes require a session.

    Migration

    Is createdBy gone?

    No. It remains the user id. creator is an optional public snapshot for UI.

    Ops

    Why do MCP lists still show UUIDs on cards?

    MCP list omits viewerUserId, so attachOpportunityFeedFields is not applied on that path.

    Related documentation

    Related documentation

    Opportunities

    Prerequisite: product loop, feed card actions, and enabled-type gates.

    Discovery Mutation Sync

    Depends-on: cache tags, revalidatePath, and Tunnel opportunity:* after mutations.

    Ring Tasks

    Same-workflow: convert-to-request writes metadata.source consumed by list cards.

    Entities

    Same-workflow: organization linkage required on create (`organizationId` / linkedEntity).

    1. Docs
    2. /API
    3. /Opportunities API

    Updated Oct 26, 20265 min listen

    Opportunities API

    REST for the dual-nature marketplace. The browse list is open to visitors; everything else needs a session. Browse list (GET /api/opportunities) is role-aware cursor pagination plus per-viewer enrichment. Search is a separate route. Like / Save / Hide / Contact are Server Actions, not HTTP resources.

    Use Founder / Developer tabs to filter this page. Product loop and card UX: Opportunities.

    Not seven types

    Older API copy listed a fixed “7 types.” Runtime types are the OpportunityType union in features/opportunities/types/index.ts, gated by ring-config.json → opportunities.enabledTypes. Do not document a closed set of seven.

    Surfaces (shared)

    MethodPathAuthNotes
    GET/api/opportunitiesOptionallimit, startAfter. Filter params types/type, q/query, categories/category, location, priority switch to the search service. Passes viewerUserId when signed in. Returns { opportunities, items, lastVisible, cursor, hasMore }
    POST/api/opportunitiesSessionBody createOpportunityBodySchema → { opportunity } 201
    GET/api/opportunities/{id}SessionSingle listing
    PUT / PATCH/api/opportunities/{id}SessionUpdate
    DELETE/api/opportunities/{id}SessionDelete
    GET/api/opportunities/searchSessionRequires q. Optional types, categories, location, limit
    POST/api/opportunities/searchSessionJSON body SearchOpportunitiesParams
    POST/api/opportunities/{id}/signSessionAdvisory petition signature, one per user. Returns signatureCount, signatureTarget
    GET/api/opportunities/mySessionOwner list; view= all | drafts | pending | active | archived
    POST/api/opportunities/uploadSessionAttachments
    GET / POST/api/mcp/v1/opportunitiesMCP guardList does not pass viewerUserId today

    Since 2026-10-17, unauthenticated GET /api/opportunities returns public, non-confidential rows for role visitor. The visitor filter runs after the page is fetched, so a guest page can hold fewer than limit rows. Detail, search and mutations still return 401 without a session.

    What operators need to know

    • Browse vs search. The /opportunities page loads GET /api/opportunities (a page of listings). Right-rail filters are sent as query params and applied on the server. Visitors can browse without an account.
    • Hidden listings. When a member taps Hide, the next browse/search for that account omits the id. Other members still see it.
    • Names on cards. List and search responses attach a public-safe creator snapshot so the UI never prints a raw UUID.
    • Create in product. The add-opportunity form still prefers the createOpportunity Server Action (app/_actions/opportunities.ts). REST POST is for API clients and MCP; both call the same service.

    Feature loop

    Matcher, enabled types, feed card actions, My Opportunities.

    Discovery sync

    Cache tags, revalidatePath, Tunnel opportunity:* after create/update/delete.

    List enrichment

    GET /api/opportunities (app/api/opportunities/route.ts):

    1. auth(). No session means role visitor, no viewer enrichment.
    2. With filter params: searchOpportunities. Otherwise getOpportunitiesForRole({ userRole, limit, startAfter, viewerUserId }).
    3. If viewerUserId is set: getViewerHiddenOpportunityIds then DatabaseService filter { field: 'id', operator: 'not-in', value: hiddenIds }.
    4. attachOpportunityFeedFields adds creator, viewer, likes.

    searchOpportunities reads session.user.id (or MCP actor id) internally — callers do not pass viewerUserId. Hidden exclusion and attach run when that id exists.

    MCP GET /api/mcp/v1/opportunities calls getOpportunitiesForRole without viewerUserId (no hide filter, no creator/viewer on that list).

    SerializedOpportunity extras (list/search)

    Canonical type file: features/opportunities/types/index.ts. Dates are ISO strings. Firestore Timestamp shapes exist only for DB_BACKEND_MODE=firebase-full.

    Create body (required)

    createOpportunityBodySchema (features/opportunities/lib/create-opportunity-schema.ts): title, briefDescription, organizationId, category, location, visibility, contactInfo.linkedEntity, contactInfo.contactAccount.

    POST returns 201 { opportunity }. Create then calls syncOpportunityDiscovery with a snippet that includes creator { id, name, avatar } from the session (live-inserted cards).

    Interactions (not REST)

    Access control

    • Visibility on the document: public | subscriber | member | confidential | entity. entity rows are readable only by admins, the poster and members of the linked entity. List queries apply role-aware visibility filters (buildOpportunityVisibilityFilters).
    • Confidential listings require confidential-capable roles.
    • Apply is in-app (no dedicated /apply REST route in alpha).

    Frequently asked questions

    Impact

    Can I list opportunities without a session?

    Yes, public non-confidential rows only. GET /api/opportunities/{id} and search still require a session.

    Does search honor Hide?

    Yes when searchOpportunities has a session (or MCP actor) user id. The HTTP GET/POST search routes require a session.

    Migration

    Is createdBy gone?

    No. It remains the user id. creator is an optional public snapshot for UI.

    Ops

    Why do MCP lists still show UUIDs on cards?

    MCP list omits viewerUserId, so attachOpportunityFeedFields is not applied on that path.

    Related documentation

    Related documentation

    Opportunities

    Prerequisite: product loop, feed card actions, and enabled-type gates.

    Discovery Mutation Sync

    Depends-on: cache tags, revalidatePath, and Tunnel opportunity:* after mutations.

    Ring Tasks

    Same-workflow: convert-to-request writes metadata.source consumed by list cards.

    Entities

    Same-workflow: organization linkage required on create (`organizationId` / linkedEntity).

    1. Docs
    2. /API
    3. /Opportunities API

    Updated Oct 26, 20265 min listen

    Opportunities API

    REST for the dual-nature marketplace. The browse list is open to visitors; everything else needs a session. Browse list (GET /api/opportunities) is role-aware cursor pagination plus per-viewer enrichment. Search is a separate route. Like / Save / Hide / Contact are Server Actions, not HTTP resources.

    Use Founder / Developer tabs to filter this page. Product loop and card UX: Opportunities.

    Not seven types

    Older API copy listed a fixed “7 types.” Runtime types are the OpportunityType union in features/opportunities/types/index.ts, gated by ring-config.json → opportunities.enabledTypes. Do not document a closed set of seven.

    Surfaces (shared)

    MethodPathAuthNotes
    GET/api/opportunitiesOptionallimit, startAfter. Filter params types/type, q/query, categories/category, location, priority switch to the search service. Passes viewerUserId when signed in. Returns { opportunities, items, lastVisible, cursor, hasMore }
    POST/api/opportunitiesSessionBody createOpportunityBodySchema → { opportunity } 201
    GET/api/opportunities/{id}SessionSingle listing
    PUT / PATCH/api/opportunities/{id}SessionUpdate
    DELETE/api/opportunities/{id}SessionDelete
    GET/api/opportunities/searchSessionRequires q. Optional types, categories, location, limit
    POST/api/opportunities/searchSessionJSON body SearchOpportunitiesParams
    POST/api/opportunities/{id}/signSessionAdvisory petition signature, one per user. Returns signatureCount, signatureTarget
    GET/api/opportunities/mySessionOwner list; view= all | drafts | pending | active | archived
    POST/api/opportunities/uploadSessionAttachments
    GET / POST/api/mcp/v1/opportunitiesMCP guardList does not pass viewerUserId today

    Since 2026-10-17, unauthenticated GET /api/opportunities returns public, non-confidential rows for role visitor. The visitor filter runs after the page is fetched, so a guest page can hold fewer than limit rows. Detail, search and mutations still return 401 without a session.

    What operators need to know

    • Browse vs search. The /opportunities page loads GET /api/opportunities (a page of listings). Right-rail filters are sent as query params and applied on the server. Visitors can browse without an account.
    • Hidden listings. When a member taps Hide, the next browse/search for that account omits the id. Other members still see it.
    • Names on cards. List and search responses attach a public-safe creator snapshot so the UI never prints a raw UUID.
    • Create in product. The add-opportunity form still prefers the createOpportunity Server Action (app/_actions/opportunities.ts). REST POST is for API clients and MCP; both call the same service.

    Feature loop

    Matcher, enabled types, feed card actions, My Opportunities.

    Discovery sync

    Cache tags, revalidatePath, Tunnel opportunity:* after create/update/delete.

    List enrichment

    GET /api/opportunities (app/api/opportunities/route.ts):

    1. auth(). No session means role visitor, no viewer enrichment.
    2. With filter params: searchOpportunities. Otherwise getOpportunitiesForRole({ userRole, limit, startAfter, viewerUserId }).
    3. If viewerUserId is set: getViewerHiddenOpportunityIds then DatabaseService filter { field: 'id', operator: 'not-in', value: hiddenIds }.
    4. attachOpportunityFeedFields adds creator, viewer, likes.

    searchOpportunities reads session.user.id (or MCP actor id) internally — callers do not pass viewerUserId. Hidden exclusion and attach run when that id exists.

    MCP GET /api/mcp/v1/opportunities calls getOpportunitiesForRole without viewerUserId (no hide filter, no creator/viewer on that list).

    SerializedOpportunity extras (list/search)

    Canonical type file: features/opportunities/types/index.ts. Dates are ISO strings. Firestore Timestamp shapes exist only for DB_BACKEND_MODE=firebase-full.

    Create body (required)

    createOpportunityBodySchema (features/opportunities/lib/create-opportunity-schema.ts): title, briefDescription, organizationId, category, location, visibility, contactInfo.linkedEntity, contactInfo.contactAccount.

    POST returns 201 { opportunity }. Create then calls syncOpportunityDiscovery with a snippet that includes creator { id, name, avatar } from the session (live-inserted cards).

    Interactions (not REST)

    Access control

    • Visibility on the document: public | subscriber | member | confidential | entity. entity rows are readable only by admins, the poster and members of the linked entity. List queries apply role-aware visibility filters (buildOpportunityVisibilityFilters).
    • Confidential listings require confidential-capable roles.
    • Apply is in-app (no dedicated /apply REST route in alpha).

    Frequently asked questions

    Impact

    Can I list opportunities without a session?

    Yes, public non-confidential rows only. GET /api/opportunities/{id} and search still require a session.

    Does search honor Hide?

    Yes when searchOpportunities has a session (or MCP actor) user id. The HTTP GET/POST search routes require a session.

    Migration

    Is createdBy gone?

    No. It remains the user id. creator is an optional public snapshot for UI.

    Ops

    Why do MCP lists still show UUIDs on cards?

    MCP list omits viewerUserId, so attachOpportunityFeedFields is not applied on that path.

    Related documentation

    Related documentation

    Opportunities

    Prerequisite: product loop, feed card actions, and enabled-type gates.

    Discovery Mutation Sync

    Depends-on: cache tags, revalidatePath, and Tunnel opportunity:* after mutations.

    Ring Tasks

    Same-workflow: convert-to-request writes metadata.source consumed by list cards.

    Entities

    Same-workflow: organization linkage required on create (`organizationId` / linkedEntity).

    1. Docs
    2. /API
    3. /Opportunities API

    Updated Oct 26, 20265 min listen

    API examples

    curl against session cookies on a local clone.

    ActionModule
    LiketoggleOpportunityLike → like-service / likes
    SavetoggleOpportunitySave → user_content_interactions
    HidemarkOpportunityNotInterested → user_content_interactions
    CommentcreateOpportunityComment
    Contact intentrecordOpportunityContactIntent (Matcher; UI still router.push /messages?user=)

    File: app/_actions/opportunity-interactions.ts.

    Mapping and not-in

    mapDbQueryRowToSerializedOpportunity() in features/opportunities/lib/opportunity-db-mapper.ts. Do not use lib/converters/opportunity-converter.ts on Postgres.

    Operator 'not-in' is on IDatabaseService DatabaseFilterOperator. PostgreSQL: NOT (field = ANY($n)). Firebase adapter post-filters.

    Post-mutation discovery sync

    Creates, updates, deletes, and status changes call syncOpportunityDiscovery() which:

    1. revalidateTag — invalidates unstable_cache opportunity lists (lib/cached-data.ts)
    2. revalidatePath — /[locale]/opportunities, /[locale]/opportunities/[id], /[locale]/opportunities/my
    3. Tunnel — opportunity:created|updated|deleted on channel opportunities

    There is no separate search-index reindex command. See Discovery mutation sync. Client browse still uses useCursorFeed (24h); revalidatePath alone does not clear that cache.

    Examples

    UI create: useActionState + createOpportunity from app/_actions/opportunities.ts — API integration.

    API Integration Examples

    Next-step: curl against a local session cookie.

    Authentication

    Depends-on: session cookie required for every opportunities route.

    API examples

    curl against session cookies on a local clone.

    ActionModule
    LiketoggleOpportunityLike → like-service / likes
    SavetoggleOpportunitySave → user_content_interactions
    HidemarkOpportunityNotInterested → user_content_interactions
    CommentcreateOpportunityComment
    Contact intentrecordOpportunityContactIntent (Matcher; UI still router.push /messages?user=)

    File: app/_actions/opportunity-interactions.ts.

    Mapping and not-in

    mapDbQueryRowToSerializedOpportunity() in features/opportunities/lib/opportunity-db-mapper.ts. Do not use lib/converters/opportunity-converter.ts on Postgres.

    Operator 'not-in' is on IDatabaseService DatabaseFilterOperator. PostgreSQL: NOT (field = ANY($n)). Firebase adapter post-filters.

    Post-mutation discovery sync

    Creates, updates, deletes, and status changes call syncOpportunityDiscovery() which:

    1. revalidateTag — invalidates unstable_cache opportunity lists (lib/cached-data.ts)
    2. revalidatePath — /[locale]/opportunities, /[locale]/opportunities/[id], /[locale]/opportunities/my
    3. Tunnel — opportunity:created|updated|deleted on channel opportunities

    There is no separate search-index reindex command. See Discovery mutation sync. Client browse still uses useCursorFeed (24h); revalidatePath alone does not clear that cache.

    Examples

    UI create: useActionState + createOpportunity from app/_actions/opportunities.ts — API integration.

    API Integration Examples

    Next-step: curl against a local session cookie.

    Authentication

    Depends-on: session cookie required for every opportunities route.

    API examples

    curl against session cookies on a local clone.

    ActionModule
    LiketoggleOpportunityLike → like-service / likes
    SavetoggleOpportunitySave → user_content_interactions
    HidemarkOpportunityNotInterested → user_content_interactions
    CommentcreateOpportunityComment
    Contact intentrecordOpportunityContactIntent (Matcher; UI still router.push /messages?user=)

    File: app/_actions/opportunity-interactions.ts.

    Mapping and not-in

    mapDbQueryRowToSerializedOpportunity() in features/opportunities/lib/opportunity-db-mapper.ts. Do not use lib/converters/opportunity-converter.ts on Postgres.

    Operator 'not-in' is on IDatabaseService DatabaseFilterOperator. PostgreSQL: NOT (field = ANY($n)). Firebase adapter post-filters.

    Post-mutation discovery sync

    Creates, updates, deletes, and status changes call syncOpportunityDiscovery() which:

    1. revalidateTag — invalidates unstable_cache opportunity lists (lib/cached-data.ts)
    2. revalidatePath — /[locale]/opportunities, /[locale]/opportunities/[id], /[locale]/opportunities/my
    3. Tunnel — opportunity:created|updated|deleted on channel opportunities

    There is no separate search-index reindex command. See Discovery mutation sync. Client browse still uses useCursorFeed (24h); revalidatePath alone does not clear that cache.

    Examples

    UI create: useActionState + createOpportunity from app/_actions/opportunities.ts — API integration.

    API Integration Examples

    Next-step: curl against a local session cookie.

    Authentication

    Depends-on: session cookie required for every opportunities route.