Opportunity Details API
Overviewβ
The Opportunity Details API allows users to retrieve comprehensive information about specific opportunities in the Ring Platform. This endpoint supports role-based access control and respects confidential opportunity visibility settings.
Endpoint Detailsβ
- URL:
/api/opportunities/[id]
- Methods:
GET
,PUT
,DELETE
- Authentication: Required (JWT Token)
- Rate Limit: 120 requests per minute for GET, 30 for PUT/DELETE
Ring Platform Conceptsβ
What are Opportunities?β
Opportunities are the core value exchange mechanism in Ring Platform's professional networking ecosystem:
Dual Nature Systemβ
- Offers: Job postings, service offerings, collaboration proposals
- Requests: Seeking services, talent acquisition, partnership requests
Confidential Access Tiersβ
- Public: Open access for basic opportunities
- Subscriber: Registered user access to standard opportunities
- Member: Verified member access to premium opportunities
- Confidential: Exclusive access to sensitive, high-value opportunities
Rich Context Frameworkβ
- Budget Ranges: Transparent pricing and compensation
- Required Skills: Detailed technical and soft skill requirements
- Documents: Supporting materials and attachments
- Expiration Management: Time-bound opportunities with status tracking
- Entity Linking: Direct connection to posting organizations
Professional Networking Focusβ
Unlike general job boards, Ring Platform opportunities are designed for:
- B2B Collaborations: Company-to-company partnerships
- Specialized Talent: High-skill technical positions
- Industry Connections: Sector-specific networking
- Confidential Deals: Sensitive business opportunities
GET - Retrieve Opportunity Detailsβ
URL Parametersβ
id
(string, required): The unique identifier of the opportunity
Authentication Requirementsβ
- All Users: Can access public opportunities
- MEMBER+: Can access member-level opportunities
- CONFIDENTIAL+: Can access confidential opportunities
- ADMIN: Full access to all opportunities
Request Formatβ
GET /api/opportunities/{opportunity_id}
Authorization: Bearer <jwt_token>
Response Formatβ
Success Response (200 OK)β
{
"id": "opp_123456789",
"type": "offer",
"title": "Senior Blockchain Developer - DeFi Platform",
"isConfidential": false,
"briefDescription": "Join our team to build next-generation DeFi solutions",
"fullDescription": "We are seeking an experienced blockchain developer to lead the development of our innovative DeFi platform. You will work with cutting-edge technologies including Ethereum, Solidity, and Web3.js to create secure and scalable financial applications.",
"createdBy": "user_987654321",
"organizationId": "entity_456789123",
"dateCreated": "2025-01-14T10:00:00Z",
"dateUpdated": "2025-01-14T15:30:00Z",
"expirationDate": "2025-02-14T23:59:59Z",
"status": "active",
"category": "Software Development",
"tags": ["blockchain", "defi", "solidity", "ethereum", "web3"],
"location": "Remote / San Francisco, CA",
"budget": {
"min": 120000,
"max": 180000,
"currency": "USD"
},
"requiredSkills": [
"Solidity programming",
"Smart contract development",
"Web3.js/Ethers.js",
"DeFi protocols",
"Security best practices"
],
"requiredDocuments": [
"Resume/CV",
"Portfolio of blockchain projects",
"Code samples"
],
"attachments": [
{
"url": "https://storage.ring.ck.ua/opportunities/job-description.pdf",
"name": "Detailed Job Description.pdf"
}
],
"visibility": "member",
"contactInfo": {
"linkedEntity": "entity_456789123",
"contactAccount": "user_987654321"
}
}
Error Responsesβ
// 401 Unauthorized
{
"error": "Unauthorized"
}
// 403 Forbidden - Insufficient access level
{
"error": "Access denied"
}
// 404 Not Found
{
"error": "Opportunity not found"
}
// 500 Internal Server Error
{
"error": "Internal Server Error"
}
Code Examplesβ
JavaScript/TypeScriptβ
interface OpportunityDetails {
id: string;
type: 'offer' | 'request';
title: string;
isConfidential: boolean;
briefDescription: string;
fullDescription?: string;
createdBy: string;
organizationId: string;
dateCreated: string;
dateUpdated: string;
expirationDate: string;
status: 'active' | 'closed' | 'expired';
category: string;
tags: string[];
location: string;
budget?: {
min: number;
max: number;
currency: string;
};
requiredSkills: string[];
requiredDocuments: string[];
attachments: Array<{
url: string;
name: string;
}>;
visibility: 'public' | 'subscriber' | 'member' | 'confidential';
contactInfo: {
linkedEntity: string;
contactAccount: string;
};
}
async function getOpportunityDetails(opportunityId: string): Promise<OpportunityDetails> {
const response = await fetch(`/api/opportunities/${opportunityId}`, {
headers: {
'Authorization': `Bearer ${getAuthToken()}`
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to fetch opportunity');
}
return response.json();
}
// Usage
try {
const opportunity = await getOpportunityDetails('opp_123456789');
console.log('Opportunity:', opportunity.title);
console.log('Budget:', `${opportunity.budget?.min}-${opportunity.budget?.max} ${opportunity.budget?.currency}`);
} catch (error) {
console.error('Error:', error.message);
}
React Component Exampleβ
import { useState, useEffect } from 'react';
interface OpportunityDetailsProps {
opportunityId: string;
}
function OpportunityDetailsPage({ opportunityId }: OpportunityDetailsProps) {
const [opportunity, setOpportunity] = useState<OpportunityDetails | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchOpportunity() {
try {
setLoading(true);
const data = await getOpportunityDetails(opportunityId);
setOpportunity(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
}
fetchOpportunity();
}, [opportunityId]);
if (loading) return <div>Loading opportunity details...</div>;
if (error) return <div>Error: {error}</div>;
if (!opportunity) return <div>Opportunity not found</div>;
return (
<div className="opportunity-details">
<header>
<h1>{opportunity.title}</h1>
<div className="opportunity-meta">
<span className={`type ${opportunity.type}`}>
{opportunity.type.toUpperCase()}
</span>
{opportunity.isConfidential && (
<span className="confidential">CONFIDENTIAL</span>
)}
<span className="status">{opportunity.status}</span>
</div>
</header>
<section className="description">
<h2>Description</h2>
<p>{opportunity.briefDescription}</p>
{opportunity.fullDescription && (
<div className="full-description">
{opportunity.fullDescription}
</div>
)}
</section>
<section className="details">
<div className="detail-grid">
<div>
<h3>Location</h3>
<p>{opportunity.location}</p>
</div>
<div>
<h3>Category</h3>
<p>{opportunity.category}</p>
</div>
{opportunity.budget && (
<div>
<h3>Budget</h3>
<p>
{opportunity.budget.min.toLocaleString()} - {opportunity.budget.max.toLocaleString()} {opportunity.budget.currency}
</p>
</div>
)}
<div>
<h3>Expires</h3>
<p>{new Date(opportunity.expirationDate).toLocaleDateString()}</p>
</div>
</div>
</section>
<section className="skills">
<h3>Required Skills</h3>
<div className="skill-tags">
{opportunity.requiredSkills.map((skill, index) => (
<span key={index} className="skill-tag">{skill}</span>
))}
</div>
</section>
<section className="tags">
<h3>Tags</h3>
<div className="tags-list">
{opportunity.tags.map((tag, index) => (
<span key={index} className="tag">#{tag}</span>
))}
</div>
</section>
{opportunity.attachments.length > 0 && (
<section className="attachments">
<h3>Attachments</h3>
<ul>
{opportunity.attachments.map((attachment, index) => (
<li key={index}>
<a href={attachment.url} target="_blank" rel="noopener noreferrer">
{attachment.name}
</a>
</li>
))}
</ul>
</section>
)}
<section className="actions">
<button className="apply-button">Apply Now</button>
<button className="contact-button">Contact Poster</button>
<button className="save-button">Save Opportunity</button>
</section>
</div>
);
}
cURL Exampleβ
# Get opportunity details
curl -X GET https://ring.ck.ua/api/opportunities/opp_123456789 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
PUT - Update Opportunityβ
Authentication Requirementsβ
- Opportunity Creator: User who created the opportunity
- ADMIN: Full platform administration access
- CONFIDENTIAL: Can update confidential opportunities
Request Formatβ
PUT /api/opportunities/{opportunity_id}
Content-Type: application/json
Authorization: Bearer <jwt_token>
Request Body Schemaβ
interface UpdateOpportunityRequest {
title?: string;
briefDescription?: string;
fullDescription?: string;
category?: string;
tags?: string[];
location?: string;
budget?: {
min: number;
max: number;
currency: string;
};
requiredSkills?: string[];
requiredDocuments?: string[];
expirationDate?: string;
visibility?: 'public' | 'subscriber' | 'member' | 'confidential';
status?: 'active' | 'closed' | 'expired';
}
Example Request Bodyβ
{
"title": "Senior Blockchain Developer - DeFi Platform (Updated)",
"briefDescription": "Join our growing team to build next-generation DeFi solutions",
"budget": {
"min": 130000,
"max": 190000,
"currency": "USD"
},
"requiredSkills": [
"Solidity programming",
"Smart contract development",
"Web3.js/Ethers.js",
"DeFi protocols",
"Security best practices",
"Layer 2 solutions"
],
"tags": ["blockchain", "defi", "solidity", "ethereum", "web3", "layer2"]
}
Response Formatβ
Success Response (200 OK)β
{
"id": "opp_123456789",
"title": "Senior Blockchain Developer - DeFi Platform (Updated)",
"briefDescription": "Join our growing team to build next-generation DeFi solutions",
"budget": {
"min": 130000,
"max": 190000,
"currency": "USD"
},
"dateUpdated": "2025-01-14T16:45:00Z",
// ... other fields
}
Code Exampleβ
async function updateOpportunity(
opportunityId: string,
updates: UpdateOpportunityRequest
): Promise<OpportunityDetails> {
const response = await fetch(`/api/opportunities/${opportunityId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAuthToken()}`
},
body: JSON.stringify(updates)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to update opportunity');
}
return response.json();
}
// Usage
try {
const updatedOpportunity = await updateOpportunity('opp_123456789', {
title: 'Updated Opportunity Title',
budget: { min: 130000, max: 190000, currency: 'USD' }
});
console.log('Updated:', updatedOpportunity.title);
} catch (error) {
console.error('Update failed:', error.message);
}
DELETE - Delete Opportunityβ
Authentication Requirementsβ
- Opportunity Creator: User who created the opportunity
- ADMIN: Full platform administration access
- CONFIDENTIAL: Can delete confidential opportunities
Request Formatβ
DELETE /api/opportunities/{opportunity_id}
Authorization: Bearer <jwt_token>
Response Formatβ
Success Response (200 OK)β
{
"message": "Opportunity deleted successfully"
}
Code Exampleβ
async function deleteOpportunity(opportunityId: string): Promise<void> {
const response = await fetch(`/api/opportunities/${opportunityId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${getAuthToken()}`
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to delete opportunity');
}
const result = await response.json();
console.log(result.message);
}
// Usage with confirmation
async function handleDeleteOpportunity(opportunityId: string) {
if (confirm('Are you sure you want to delete this opportunity? This action cannot be undone.')) {
try {
await deleteOpportunity(opportunityId);
// Redirect or refresh list
window.location.href = '/opportunities';
} catch (error) {
alert(`Failed to delete opportunity: ${error.message}`);
}
}
}
Access Control Logicβ
Confidential Access Checkβ
function hasConfidentialAccess(userRole: string): boolean {
return ['admin', 'confidential'].includes(userRole);
}
function canAccessOpportunity(user: User, opportunity: Opportunity): boolean {
// Public opportunities - everyone can access
if (opportunity.visibility === 'public') return true;
// Subscriber level - registered users
if (opportunity.visibility === 'subscriber' && user.role !== 'visitor') return true;
// Member level - verified members and above
if (opportunity.visibility === 'member' && ['member', 'confidential', 'admin'].includes(user.role)) return true;
// Confidential level - premium access only
if (opportunity.visibility === 'confidential' && hasConfidentialAccess(user.role)) return true;
return false;
}
Validation Rulesβ
Opportunity Data Validationβ
title
: 1-200 charactersbriefDescription
: 1-500 charactersfullDescription
: Optional, max 5000 charactersbudget.min
: Must be positive numberbudget.max
: Must be greater than minexpirationDate
: Must be future daterequiredSkills
: Max 20 skills, each 1-100 characterstags
: Max 10 tags, each 1-50 characters
Security Considerationsβ
- Role-Based Access: Strict visibility controls based on user roles
- Input Validation: All input is validated and sanitized
- Authorization Checks: Verify user permissions before operations
- Audit Logging: All modifications are logged
- Rate Limiting: Prevents abuse with appropriate limits
Related Endpointsβ
- List Opportunities - Browse all opportunities
- Create Opportunity - Create new opportunity
- Confidential Opportunities - Premium access
- Opportunity Upload - File attachments
Changelogβ
- v1.0.0 - Initial implementation with basic CRUD operations
- v1.1.0 - Added confidential access controls
- v1.2.0 - Enhanced validation and error handling
- v1.3.0 - Added budget and skills management
- v1.4.0 - Improved security and audit logging