openapi: 3.1.0 info: title: SyriaGraph Public API version: 0.1.0 description: | Read-only access to the published SyriaGraph graph. Every relationship carries its evidence. A documented relationship is not an accusation of wrongdoing; transparency signals identify patterns for further investigation only. Limits: graph depth ≤ 3, ≤ 150 nodes per response, path length ≤ 6, ≤ 5 paths; per-client rate limits (HTTP 429 with Retry-After). IDs are UUIDs; unknown or unpublished IDs return 404. servers: - url: / paths: /api/search: get: summary: Search people, companies, institutions, positions, contracts and projects (Arabic/English, aliases, transliterations) parameters: - { name: q, in: query, required: true, schema: { type: string, maxLength: 200 } } - { name: types, in: query, description: Comma-separated entity labels, schema: { type: string, example: "Person,Company" } } responses: "200": description: Results grouped by type content: application/json: schema: type: object properties: query: { type: string } results: type: object properties: people: { type: array, items: { $ref: "#/components/schemas/Entity" } } companies: { type: array, items: { $ref: "#/components/schemas/Entity" } } institutions: { type: array, items: { $ref: "#/components/schemas/Entity" } } positions: { type: array, items: { $ref: "#/components/schemas/Entity" } } contracts: { type: array, items: { $ref: "#/components/schemas/Entity" } } projects: { type: array, items: { $ref: "#/components/schemas/Entity" } } "429": { $ref: "#/components/responses/RateLimited" } /api/entity/{id}: get: summary: Any entity with summary counts parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": { $ref: "#/components/responses/EntityWithSummary" } "404": { $ref: "#/components/responses/NotFound" } /api/person/{id}: get: summary: Person (404 if the id is not a person) parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": { $ref: "#/components/responses/EntityWithSummary" } "404": { $ref: "#/components/responses/NotFound" } /api/company/{id}: get: summary: Company parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": { $ref: "#/components/responses/EntityWithSummary" } "404": { $ref: "#/components/responses/NotFound" } /api/organization/{id}: get: summary: Organization / institution parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": { $ref: "#/components/responses/EntityWithSummary" } "404": { $ref: "#/components/responses/NotFound" } /api/entity/{id}/connections: get: summary: Direct documented relationships parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": description: Connections content: application/json: schema: type: object properties: connections: type: array items: type: object properties: edge: { $ref: "#/components/schemas/Edge" } other: { $ref: "#/components/schemas/Entity" } direction: { type: string, enum: [out, in] } /api/entity/{id}/timeline: get: summary: Chronological events derived from dated relationships and entity dates parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": description: Timeline content: application/json: schema: type: object properties: timeline: type: array items: type: object properties: date: { type: string } end: { type: string } kind: { type: string, enum: [relationship, entity] } text: { type: string } edge: { $ref: "#/components/schemas/Edge" } /api/graph/{id}: get: summary: Bounded neighbourhood subgraph for the explorer parameters: - { $ref: "#/components/parameters/Id" } - { name: depth, in: query, schema: { type: integer, minimum: 1, maximum: 3, default: 1 } } - { name: types, in: query, description: Comma-separated relationship types to include, schema: { type: string } } - { name: labels, in: query, description: Comma-separated entity labels to include, schema: { type: string } } responses: "200": description: Subgraph content: application/json: schema: { $ref: "#/components/schemas/Subgraph" } "404": { $ref: "#/components/responses/NotFound" } "429": { $ref: "#/components/responses/RateLimited" } "503": { description: Query too expensive (timeout) } /api/path: get: summary: Shortest documented paths between two entities parameters: - { name: from, in: query, required: true, schema: { type: string, format: uuid } } - { name: to, in: query, required: true, schema: { type: string, format: uuid } } - { name: max, in: query, schema: { type: integer, minimum: 1, maximum: 6, default: 6 } } responses: "200": description: Paths content: application/json: schema: type: object properties: paths: type: array items: type: object properties: nodes: { type: array, items: { $ref: "#/components/schemas/Entity" } } edges: { type: array, items: { $ref: "#/components/schemas/Edge" } } "429": { $ref: "#/components/responses/RateLimited" } /api/evidence/{rid}: get: summary: Evidence (supporting and disputing) for a relationship or entity parameters: [{ name: rid, in: path, required: true, schema: { type: string, format: uuid } }] responses: "200": description: Evidence content: application/json: schema: type: object properties: evidence: { type: array, items: { $ref: "#/components/schemas/Evidence" } } /api/sources: get: summary: Published public sources responses: "200": { description: Sources } /api/sources/{id}: get: summary: A source and the claims citing it parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": { description: Source with uses } "404": { $ref: "#/components/responses/NotFound" } /api/signals/{id}: get: summary: Transparency signals (patterns for investigation, not findings) parameters: [{ $ref: "#/components/parameters/Id" }] responses: "200": description: Signals with the documented path that produced each content: application/json: schema: type: object properties: disclaimer: { type: string } signals: type: array items: type: object properties: kind: { type: string, enum: [official_owns_contractor, relative_owns_contractor, shared_directors, repeat_contracts, ownership_chain] } title: { type: string } explanation: { type: string } sourceCount: { type: integer } path: { $ref: "#/components/schemas/Subgraph" } /api/stats: get: summary: Counts computed from the published graph responses: "200": { description: Counts } /api/submit/challenge: get: summary: Proof-of-work challenge for the anonymous submission form (served by the relay) responses: "200": { description: "{ challenge, bits }" } /api/submit: post: summary: Anonymous submission (relay). No account, no identifying data required. requestBody: content: multipart/form-data: schema: type: object required: [text, challenge, nonce] properties: text: { type: string, minLength: 10, maxLength: 20000 } people: { type: string } organizations: { type: string } url: { type: string } contact: { type: string } anonymous: { type: string } challenge: { type: string } nonce: { type: string } files: { type: array, items: { type: string, format: binary }, maxItems: 10 } responses: "200": { description: "{ submission_id, secret } — shown once; the secret cannot be recovered" } "400": { description: Validation or anti-abuse failure } "413": { description: Upload too large } "429": { $ref: "#/components/responses/RateLimited" } /api/mailbox/open: post: summary: Read researcher replies with submission ID + secret (relay) requestBody: content: application/json: schema: { type: object, required: [submission_id, secret], properties: { submission_id: { type: string }, secret: { type: string } } } responses: "200": { description: Messages } "401": { description: Unknown ID or wrong secret (indistinguishable) } /api/mailbox/reply: post: summary: Send more information to researchers (relay) requestBody: content: multipart/form-data: schema: type: object required: [submission_id, secret, text] properties: submission_id: { type: string } secret: { type: string } text: { type: string } files: { type: array, items: { type: string, format: binary } } responses: "200": { description: Stored (encrypted) } "401": { description: Unknown ID or wrong secret } components: parameters: Id: { name: id, in: path, required: true, schema: { type: string, format: uuid } } responses: NotFound: { description: Not found or not published } RateLimited: { description: Too many requests } EntityWithSummary: description: Entity content: application/json: schema: type: object properties: entity: { $ref: "#/components/schemas/Entity" } summary: type: object properties: connections: { type: integer } companies: { type: integer } positions: { type: integer } contractsInNetwork: { type: integer } sources: { type: integer } currentPosition: { type: string } schemas: Entity: type: object properties: id: { type: string, format: uuid } label: { type: string, enum: [Person, Organization, Company, GovernmentPosition, Contract, Project] } name: { type: string } name_ar: { type: string } confidence: { type: string, enum: [high, medium, low] } props: { type: object, additionalProperties: true } Edge: type: object properties: id: { type: string, format: uuid } type: type: string enum: [HOLDS_POSITION, AT_INSTITUTION, OWNS, SHAREHOLDER_OF, DIRECTOR_OF, FOUNDED, RELATED_TO, ASSOCIATED_WITH, APPOINTED_BY, SUBSIDIARY_OF, RECEIVED_CONTRACT, AWARDED, PART_OF, SANCTIONED_BY] from: { type: string, format: uuid } to: { type: string, format: uuid } detail: { type: string } confidence: { type: string, enum: [high, medium, low] } verification_status: { type: string, enum: [verified, partially_verified, unverified, disputed] } valid_from: { type: string } valid_to: { type: string } source_ids: { type: array, items: { type: string } } Subgraph: type: object properties: nodes: { type: array, items: { $ref: "#/components/schemas/Entity" } } edges: { type: array, items: { $ref: "#/components/schemas/Edge" } } truncated: { type: boolean } Evidence: type: object properties: id: { type: string } snippet: { type: string } stance: { type: string, enum: [supports, disputes] } accessed_at: { type: string } page_ref: { type: string } verification_status: { type: string } source: type: object properties: id: { type: string } title: { type: string } publisher: { type: string } url: { type: string } archive_url: { type: string } publication_date: { type: string } source_type: { type: string } reliability_status: { type: string } public_file: { type: string, description: Path to the sanitized published document, if any }