Перейти к основному содержимому

API-контракты

Endpoint coverage

api-map.md является endpoint registry. Этот файл не дублирует полный DTO для каждой CRUD-строки; все endpoints из api-map.md покрываются platform envelope, pagination, error и idempotency rules плюс DTO/feature docs из таблицы.

API map sectionEndpoint rows coveredContract source
Public API/pages/{slug}, /catalog/items*, /collections/{slug}, /profiles/{slug}, /reviews, /achievements, /facts, /forms/{formKey}/submissions, /sitemap.xml, /redirects/resolvePublic page, Catalog, Profiles, Reviews, achievements, facts, Forms, Redirects; features/landing-pages.md, features/catalog.md, features/public-profiles.md, features/reviews-achievements.md, features/facts.md, features/admin.md
Pages и CMS/admin/pages*, /admin/page-versions*, /admin/blocks*CMS pages; features/landing-pages.md, features/admin.md
Catalog/admin/catalog/items*, /admin/collections*Catalog; features/catalog.md
Profiles, reviews, achievements, facts/admin/profiles*, /admin/reviews*, /admin/achievements*, /admin/facts*Profiles, Reviews, achievements, facts; features/public-profiles.md, features/reviews-achievements.md, features/facts.md
Campaigns, forms, SEO/admin/campaigns, /admin/forms*, /admin/form-submissions*, /admin/redirects*, /admin/sitemap/rebuild, /admin/audit-logsForms, Redirects, common admin/audit rules; features/admin.md

Общие DTO

type PaginationQuery = {
page?: number;
perPage?: number;
sort?: string;
};

type SourceRef = {
domain: 'crm' | 'lms' | 'competitions' | 'task-bank' | 'identity' | 'management' | 'manual';
type: string;
id: string;
};

type PublicationStatus = 'draft' | 'review' | 'scheduled' | 'published' | 'hidden' | 'paused' | 'archived';

type ApiEnvelope<TData, TMeta = Record<string, unknown>> = {
data: TData | null;
meta?: TMeta;
error?: {
code: string;
message: string;
details?: Record<string, unknown>;
};
};

Public page

GET /api/v2/storefront/pages/{slug}

Response:

type PublicPageResponse = {
page: {
id: string;
slug: string;
locale: string;
type: 'landing' | 'catalog' | 'profile' | 'article' | 'campaign' | 'system';
canonicalUrl?: string;
seo: SeoDto;
blocks: PublicBlockDto[];
};
};

type SeoDto = {
title: string;
description?: string;
canonicalUrl?: string;
robots: 'index,follow' | 'noindex,nofollow' | 'noindex,follow';
openGraph?: Record<string, unknown>;
structuredData?: Record<string, unknown>;
};

type PublicBlockDto = {
id: string;
type: string;
position: number;
props: Record<string, unknown>;
};

Правила:

  • возвращается только published version;
  • private/noindex preview не возвращается public endpoint-ом;
  • blocks фильтруются по visibility rule.

CMS pages

POST /api/v2/storefront/admin/pages

type CreatePageRequest = {
slug: string;
locale?: string;
type: 'landing' | 'catalog' | 'profile' | 'article' | 'campaign' | 'system';
primarySource?: SourceRef;
canonicalUrl?: string;
indexingPolicy?: 'index' | 'noindex' | 'private';
};

Validation:

  • slug уникален в рамках locale;
  • external canonical требует allowlist;
  • private page не попадает в sitemap.

POST /api/v2/storefront/admin/page-versions/{versionId}/blocks

type CreateBlockRequest = {
type: 'hero' | 'rich_text' | 'catalog_rail' | 'profile_list' | 'reviews' | 'achievements' | 'facts' | 'roadmap' | 'faq' | 'form' | 'cta' | 'media' | 'custom_embed';
position: number;
props: Record<string, unknown>;
dataSource?: {
mode: 'manual' | 'source_ref' | 'rule_based' | 'hybrid';
source?: SourceRef;
rule?: Record<string, unknown>;
};
visibilityRule?: Record<string, unknown>;
};

POST /api/v2/storefront/admin/page-versions/{versionId}/publish

type PublishPageVersionRequest = {
scheduledFor?: string;
comment?: string;
};

Response:

type PageVersionDto = {
id: string;
pageId: string;
version: number;
status: 'draft' | 'review' | 'scheduled' | 'published' | 'retired';
publishedAt?: string;
scheduledFor?: string;
};

Catalog

GET /api/v2/storefront/catalog/items

Query:

type CatalogQuery = PaginationQuery & {
subject?: string;
format?: string;
level?: string;
age?: string;
goal?: string;
availability?: string;
priceMin?: number;
priceMax?: number;
q?: string;
};

Response:

type CatalogItemDto = {
id: string;
slug: string;
title: string;
source: SourceRef;
facets: Record<string, string | string[]>;
display: {
subtitle?: string;
description?: string;
badges?: string[];
imageUrl?: string;
cta?: CtaDto;
};
availability?: Record<string, unknown>;
pricingSummary?: {
label: string;
minPrice?: number;
maxPrice?: number;
currency?: 'RUB';
};
};

type CtaDto = {
label: string;
action: 'open_page' | 'submit_form' | 'external_link' | 'start_checkout';
href?: string;
formKey?: string;
};

POST /api/v2/storefront/admin/catalog/items

type UpsertCatalogItemRequest = {
source: SourceRef;
slug: string;
title: string;
facets: Record<string, unknown>;
display?: Record<string, unknown>;
availability?: Record<string, unknown>;
pricingSummary?: Record<string, unknown>;
};

Profiles

type PublicProfileDto = {
id: string;
type: 'person' | 'student' | 'alumni' | 'team' | 'organization' | 'committee';
slug: string;
publicName: string;
display: Record<string, unknown>;
roles: Array<{
context: SourceRef;
roleLabel: string;
position?: number;
}>;
seo: SeoDto;
};

Create request:

type CreatePublicProfileRequest = {
type: PublicProfileDto['type'];
source?: SourceRef;
slug: string;
publicName: string;
privacyPolicy?: Record<string, unknown>;
consentStatus?: 'unknown' | 'requested' | 'granted' | 'revoked' | 'not_required';
display?: Record<string, unknown>;
};

Reviews, achievements, facts

type ReviewDto = {
id: string;
authorPublicName: string;
authorType: 'student' | 'parent' | 'alumni' | 'partner' | 'other';
rating?: number;
body: string;
source?: SourceRef;
displayPlaces: string[];
moderationStatus: 'draft' | 'in_review' | 'approved' | 'rejected' | 'published' | 'hidden' | 'archived';
consentStatus: string;
};

type AchievementDto = {
id: string;
title: string;
description?: string;
type: string;
subject: SourceRef;
periodStart?: string;
periodEnd?: string;
source?: Record<string, unknown>;
moderationStatus: ReviewDto['moderationStatus'];
};

type FactDto = {
id: string;
key: string;
type: 'number' | 'text' | 'badge' | 'statistic' | 'certification' | 'media';
publicText: string;
value: Record<string, unknown>;
unit?: string;
verificationStatus: 'draft' | 'needs_review' | 'verified' | 'expired' | 'rejected';
status: PublicationStatus;
};

Moderation:

type ModerationDecisionRequest = {
decision: 'approved' | 'rejected' | 'needs_changes' | 'hidden';
reason?: string;
comment?: string;
};

Forms

POST /api/v2/storefront/forms/{formKey}/submissions

type SubmitFormRequest = {
fields: Record<string, string | number | boolean | string[]>;
consentAccepted: boolean;
utm?: Record<string, string>;
captchaToken?: string;
};

type SubmitFormResponse = {
accepted: true;
submissionId: string;
};

Validation:

  • consentAccepted = true;
  • fields соответствуют published form schema;
  • honeypot/captcha/spam policy passed;
  • response всегда нейтрален и не раскрывает CRM status.

Redirects

type RedirectDto = {
id: string;
fromPath: string;
toUrl: string;
statusCode: 301 | 302 | 307 | 308;
isExternal: boolean;
status: PublicationStatus;
};