Confidential Entities API
Overviewβ
The Confidential Entities API provides access to restricted entity information within the Ring Platform. This endpoint is reserved for users with CONFIDENTIAL or ADMIN roles and implements strict security controls to protect sensitive organizational data.
Endpoint Detailsβ
- URL:
/api/confidential/entities
- Method:
GET
- Authentication: Required (JWT Token)
- Authorization: CONFIDENTIAL or ADMIN role required
- Rate Limit: Standard API limits apply
- Caching: No-cache headers enforced for security
Ring Platform Conceptsβ
What are Confidential Entities?β
Confidential entities represent organizations with restricted access levels in the Ring Platform:
- Enhanced Privacy: Only visible to users with appropriate clearance
- Sensitive Information: May contain classified business data
- Exclusive Access: Limited to CONFIDENTIAL and ADMIN users
- High-Value Networks: Premium organizations and institutions
- Strategic Partnerships: Entities involved in sensitive collaborations
Access Controlβ
The confidential tier implements strict access controls:
- Role Verification: CONFIDENTIAL or ADMIN role required
- Session Validation: Active authentication session mandatory
- No Caching: Responses are never cached for security
- Audit Logging: All access attempts are logged
Query Parametersβ
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
page | integer | No | 1 | Page number for pagination |
limit | integer | No | 20 | Number of entities per page (max 100) |
sort | string | No | createdAt:desc | Sort field and direction (field:asc/desc) |
filter | string | No | '' | Filter by entity status |
startAfter | string | No | undefined | Entity ID for cursor-based pagination |
Supported Sort Fieldsβ
createdAt
- Creation dateupdatedAt
- Last modification datename
- Entity nameaddedBy
- Creator user IDstatus
- Entity status
Supported Filtersβ
active
- Active entities onlypending
- Entities pending approvalsuspended
- Temporarily suspended entitiesarchived
- Archived entities
Request Formatβ
Headersβ
GET /api/confidential/entities?page=1&limit=20&sort=createdAt:desc
Authorization: Bearer <jwt_token>
Example Requestβ
GET /api/confidential/entities?page=1&limit=10&sort=name:asc&filter=active
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response Formatβ
Success Response (200 OK)β
{
"entities": [
{
"id": "entity_confidential_123",
"name": "Strategic Partners Corp",
"description": "High-level strategic partnership organization",
"industryType": "technology",
"foundedYear": 2020,
"employeeCount": "51-200",
"location": "New York, NY",
"isConfidential": true,
"status": "active",
"addedBy": "user_admin_456",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-20T14:45:00Z",
"logoUrl": "https://storage.example.com/logos/strategic-partners.png",
"websiteUrl": "https://strategicpartners.example.com",
"socialLinks": {
"linkedin": "https://linkedin.com/company/strategic-partners",
"twitter": "https://twitter.com/strategicpartners"
},
"certifications": ["ISO-27001", "SOC-2"],
"partnerships": ["entity_789", "entity_321"],
"memberCount": 15,
"opportunityCount": 8
}
],
"lastVisible": "entity_confidential_123",
"totalPages": 5,
"totalEntities": 87
}
Error Responsesβ
401 Unauthorizedβ
{
"error": "Unauthorized"
}
403 Permission Deniedβ
{
"error": "Permission denied"
}
500 Internal Server Errorβ
{
"error": "Internal Server Error"
}
Code Examplesβ
JavaScript/TypeScriptβ
interface ConfidentialEntity {
id: string;
name: string;
description: string;
industryType: string;
isConfidential: true;
status: 'active' | 'pending' | 'suspended' | 'archived';
addedBy: string;
createdAt: string;
updatedAt: string;
logoUrl?: string;
websiteUrl?: string;
socialLinks?: {
linkedin?: string;
twitter?: string;
facebook?: string;
instagram?: string;
};
certifications?: string[];
partnerships?: string[];
memberCount?: number;
opportunityCount?: number;
}
interface ConfidentialEntitiesResponse {
entities: ConfidentialEntity[];
lastVisible: string | null;
totalPages: number;
totalEntities: number;
}
async function getConfidentialEntities(
page: number = 1,
limit: number = 20,
sort: string = 'createdAt:desc',
filter?: string
): Promise<ConfidentialEntitiesResponse> {
const params = new URLSearchParams({
page: page.toString(),
limit: limit.toString(),
sort,
...(filter && { filter })
});
const response = await fetch(`/api/confidential/entities?${params}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${getAuthToken()}`
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to fetch confidential entities');
}
return response.json();
}
// Usage
try {
const result = await getConfidentialEntities(1, 10, 'name:asc', 'active');
console.log(`Found ${result.totalEntities} confidential entities`);
result.entities.forEach(entity => {
console.log(`- ${entity.name} (${entity.industryType})`);
});
} catch (error) {
console.error('Error fetching confidential entities:', error.message);
}
React Hook Exampleβ
import { useState, useEffect } from 'react';
interface UseConfidentialEntitiesResult {
entities: ConfidentialEntity[];
loading: boolean;
error: string | null;
totalPages: number;
totalEntities: number;
loadMore: () => void;
hasMore: boolean;
refresh: () => void;
}
function useConfidentialEntities(
initialLimit: number = 20,
initialSort: string = 'createdAt:desc'
): UseConfidentialEntitiesResult {
const [entities, setEntities] = useState<ConfidentialEntity[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const [totalEntities, setTotalEntities] = useState(0);
const [lastVisible, setLastVisible] = useState<string | null>(null);
const fetchEntities = async (resetPage: boolean = false) => {
try {
setLoading(true);
setError(null);
const currentPage = resetPage ? 1 : page;
const result = await getConfidentialEntities(
currentPage,
initialLimit,
initialSort
);
if (resetPage) {
setEntities(result.entities);
setPage(1);
} else {
setEntities(prev => [...prev, ...result.entities]);
}
setTotalPages(result.totalPages);
setTotalEntities(result.totalEntities);
setLastVisible(result.lastVisible);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchEntities(true);
}, [initialLimit, initialSort]);
const loadMore = () => {
if (page < totalPages) {
setPage(prev => prev + 1);
fetchEntities();
}
};
const refresh = () => {
fetchEntities(true);
};
return {
entities,
loading,
error,
totalPages,
totalEntities,
loadMore,
hasMore: page < totalPages,
refresh
};
}
// Usage in component
function ConfidentialEntitiesList() {
const {
entities,
loading,
error,
totalEntities,
loadMore,
hasMore,
refresh
} = useConfidentialEntities(10, 'name:asc');
if (loading && entities.length === 0) {
return <div>Loading confidential entities...</div>;
}
if (error) {
return (
<div className="text-red-600">
Error: {error}
<button onClick={refresh} className="ml-2 px-3 py-1 bg-blue-500 text-white rounded">
Retry
</button>
</div>
);
}
return (
<div>
<div className="mb-4">
<h2>Confidential Entities ({totalEntities})</h2>
<button onClick={refresh} className="px-3 py-1 bg-gray-500 text-white rounded">
Refresh
</button>
</div>
<div className="grid gap-4">
{entities.map(entity => (
<div key={entity.id} className="border p-4 rounded-lg bg-gray-50">
<div className="flex items-center gap-3 mb-2">
{entity.logoUrl && (
<img src={entity.logoUrl} alt={entity.name} className="w-12 h-12 rounded" />
)}
<div>
<h3 className="font-semibold">{entity.name}</h3>
<span className="text-sm text-gray-600">{entity.industryType}</span>
</div>
<span className="ml-auto px-2 py-1 bg-red-100 text-red-800 text-xs rounded">
CONFIDENTIAL
</span>
</div>
<p className="text-gray-700 mb-3">{entity.description}</p>
<div className="flex gap-4 text-sm text-gray-600">
<span>π₯ {entity.memberCount} members</span>
<span>π― {entity.opportunityCount} opportunities</span>
<span>π
Founded {entity.foundedYear}</span>
</div>
</div>
))}
</div>
{hasMore && (
<button
onClick={loadMore}
disabled={loading}
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
>
{loading ? 'Loading...' : 'Load More'}
</button>
)}
</div>
);
}
cURL Exampleβ
# Get confidential entities with pagination
curl -X GET "https://ring.ck.ua/api/confidential/entities?page=1&limit=10&sort=name:asc" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Get active confidential entities only
curl -X GET "https://ring.ck.ua/api/confidential/entities?filter=active&limit=20" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Expected response
# {
# "entities": [...],
# "lastVisible": "entity_confidential_123",
# "totalPages": 5,
# "totalEntities": 87
# }
Python Exampleβ
import requests
from typing import List, Optional, Dict, Any
class ConfidentialEntitiesClient:
def __init__(self, base_url: str, token: str):
self.base_url = base_url
self.headers = {"Authorization": f"Bearer {token}"}
def get_confidential_entities(
self,
page: int = 1,
limit: int = 20,
sort: str = "createdAt:desc",
filter_status: Optional[str] = None
) -> Dict[str, Any]:
"""Fetch confidential entities with pagination"""
params = {
"page": page,
"limit": limit,
"sort": sort
}
if filter_status:
params["filter"] = filter_status
response = requests.get(
f"{self.base_url}/api/confidential/entities",
headers=self.headers,
params=params
)
if response.status_code == 401:
raise PermissionError("Unauthorized - check your authentication token")
elif response.status_code == 403:
raise PermissionError("Permission denied - CONFIDENTIAL or ADMIN role required")
elif response.status_code != 200:
response.raise_for_status()
return response.json()
def get_all_confidential_entities(
self,
limit: int = 20,
sort: str = "createdAt:desc",
filter_status: Optional[str] = None
) -> List[Dict[str, Any]]:
"""Fetch all confidential entities across all pages"""
all_entities = []
page = 1
while True:
result = self.get_confidential_entities(page, limit, sort, filter_status)
all_entities.extend(result["entities"])
if page >= result["totalPages"]:
break
page += 1
return all_entities
# Usage
client = ConfidentialEntitiesClient("https://ring.ck.ua", "your_jwt_token")
try:
# Get first page of confidential entities
result = client.get_confidential_entities(page=1, limit=10, sort="name:asc")
print(f"Found {result['totalEntities']} confidential entities")
for entity in result["entities"]:
print(f"- {entity['name']} ({entity['industryType']})")
# Get all active confidential entities
active_entities = client.get_all_confidential_entities(
limit=50,
sort="name:asc",
filter_status="active"
)
print(f"\nFound {len(active_entities)} active confidential entities")
except PermissionError as e:
print(f"Access denied: {e}")
except Exception as e:
print(f"Error: {e}")
Authorization Requirementsβ
Required Rolesβ
- CONFIDENTIAL: Premium users with confidential access
- ADMIN: Full platform administrators
Role Verification Processβ
// Authorization check logic
function hasConfidentialAccess(userRole: UserRole): boolean {
return userRole === UserRole.CONFIDENTIAL || userRole === UserRole.ADMIN;
}
Session Requirementsβ
- Valid JWT token in Authorization header
- Active session with appropriate role
- Session must not be expired
Security Considerationsβ
Access Controlβ
- Strict Role Checking: Only CONFIDENTIAL and ADMIN users allowed
- Session Validation: Active authentication session required
- No Public Access: No anonymous or guest access permitted
- Audit Logging: All access attempts logged for security monitoring
Response Securityβ
- No Caching: Cache-Control headers prevent response caching
- Sensitive Data: Contains confidential organizational information
- Rate Limiting: Standard API rate limits apply
- HTTPS Only: Secure transport required in production
Data Protectionβ
- Confidential entities marked with
isConfidential: true
- Enhanced privacy controls for sensitive information
- Restricted visibility even within the platform
- Regular security audits and access reviews
Error Handlingβ
Common Error Scenariosβ
-
Unauthorized Access (401)
- Missing JWT token
- Invalid or expired token
- Solution: Provide valid authentication
-
Permission Denied (403)
- User lacks CONFIDENTIAL or ADMIN role
- Session role insufficient
- Solution: Contact admin for role upgrade
-
Server Error (500)
- Database connection issues
- Internal service errors
- Solution: Retry request or contact support
Performance Considerationsβ
Paginationβ
- Default limit: 20 entities per page
- Maximum limit: 100 entities per page
- Cursor-based pagination with
startAfter
parameter - Total count provided for UI pagination
Cachingβ
- No client-side caching due to sensitivity
- No CDN caching for security
- Fresh data on every request
- Server-side optimization for performance
Indexingβ
- Optimized database queries for common filters
- Compound indexes for sort combinations
- Efficient pagination with Firestore cursors
Testing Examplesβ
Unit Test Exampleβ
describe('GET /api/confidential/entities', () => {
it('should return confidential entities for CONFIDENTIAL user', async () => {
const response = await request(app)
.get('/api/confidential/entities?page=1&limit=10')
.set('Authorization', `Bearer ${confidentialUserToken}`)
.expect(200);
expect(response.body).toHaveProperty('entities');
expect(response.body).toHaveProperty('totalPages');
expect(response.body).toHaveProperty('totalEntities');
expect(response.body.entities).toBeInstanceOf(Array);
// Verify all entities are marked as confidential
response.body.entities.forEach(entity => {
expect(entity.isConfidential).toBe(true);
});
});
it('should return 403 for regular user', async () => {
const response = await request(app)
.get('/api/confidential/entities')
.set('Authorization', `Bearer ${regularUserToken}`)
.expect(403);
expect(response.body.error).toBe('Permission denied');
});
it('should return 401 for unauthenticated request', async () => {
const response = await request(app)
.get('/api/confidential/entities')
.expect(401);
expect(response.body.error).toBe('Unauthorized');
});
it('should support pagination parameters', async () => {
const response = await request(app)
.get('/api/confidential/entities?page=2&limit=5&sort=name:asc')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
expect(response.body.entities.length).toBeLessThanOrEqual(5);
});
it('should support filtering by status', async () => {
const response = await request(app)
.get('/api/confidential/entities?filter=active')
.set('Authorization', `Bearer ${confidentialUserToken}`)
.expect(200);
response.body.entities.forEach(entity => {
expect(entity.status).toBe('active');
});
});
});
Related Endpointsβ
- Entity Details - Get specific entity information
- Regular Entities - Browse public entities
- Confidential Opportunities - Access confidential opportunities
- User Profile - Check user role and permissions
Best Practicesβ
For Developersβ
- Role Verification: Always verify user has appropriate role before making requests
- Error Handling: Implement proper error handling for 401/403 responses
- No Caching: Respect no-cache headers for security
- Pagination: Use proper pagination for large datasets
- Loading States: Show appropriate loading indicators
For Administratorsβ
- Access Reviews: Regularly review who has CONFIDENTIAL access
- Audit Monitoring: Monitor access logs for suspicious activity
- Role Management: Carefully manage CONFIDENTIAL role assignments
- Security Training: Ensure team understands confidential data handling
For Securityβ
- HTTPS Only: Never access over HTTP in production
- Token Management: Secure JWT token storage and transmission
- Session Monitoring: Monitor for unusual access patterns
- Data Classification: Ensure confidential entities are properly marked
Changelogβ
- v1.0.0 - Initial implementation with basic confidential access
- v1.1.0 - Added pagination and filtering support
- v1.2.0 - Enhanced security with no-cache headers
- v1.3.0 - Improved error handling and logging
- v1.4.0 - Added audit logging and access monitoring