---
title: "Contribute expert knowledge"
method: POST
path: "/pots/{pot_id}/facts/contribute"
tags: ["contribution"]
---

# Contribute expert knowledge

`POST /pots/{pot_id}/facts/contribute`

## Request

- Base URL: `https://api.scipot.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.scipot.ai/pots/{pot_id}/facts/contribute`
- Auth: none declared
- TypeScript: <https://skmtc.dev/scipot/apis/scipot-core-api/docs/pots/:pot_id/facts/contribute/post.ts> (a function with typed inputs; its response is checked against the schema)

Contribute knowledge with a contributor profile.

The contributed content is extracted into facts. Each fact receives a POT Score
based on the contributor's expertise relevance to the fact's domain:
- Score formula: 0.45 + (expertise_relevance × 0.14), at most 0.59 (INFERRED) unless endorsed
- Range: 0.45 (no expertise match) to 0.59; endorsed=true floors it at 0.85 (VERIFIED)
- `tentative` (the contributor is not sure): 0.20 + (relevance × 0.19), the
  HYPOTHESIS band; exclusive with `endorsed`

Optionally runs post-ingestion (edge detection + score propagation).

## Path parameters

- `pot_id` string, uuid, required

## Headers

- `Idempotency-Key` string, nullable

## Request body

- ContributeRequest: Request body for POST /pots/{pot_id}/facts/contribute.
  - `content` string, required: Knowledge content to contribute (will be extracted into facts)
  - `contributor_id` string, uuid, required: Contributor registry ID (must be a registered, active contributor)
  - `run_post_ingestion` boolean: Whether to run edge detection and score propagation after contribution
  - `influence_factor` number, nullable: Integrator-defined seniority influence factor (0.0-1.0). Scales the contribution score range.
  - `endorsed` boolean: Expert endorsement (a thumbs-up). When true, the contributed facts are floored at the VERIFIED level regardless of the measured expertise match — used when a domain expert directly answers a knowledge gap. The resulting high certainty also stops the gap from being re-flagged on future queries.
  - `tentative` boolean: The contributor says they are not sure of it. The facts are scored in the HYPOTHESIS band (0.20-0.39) instead of 0.45-0.59, still weighed by the measured expertise, so a guess never reads as supported and never closes a knowledge gap on its own. Cannot be combined with `endorsed`.
  - `valid_from` string, date, nullable: First day this knowledge holds in the world, in the contributor's word (ISO 8601 date). The facts carry it as `valid_from` with `valid_from_source: contributor`. Omitted = not known.
  - `basis_quote` string, nullable: The contributor's own words this contribution is based on, quoted as they said them (for example what a person told an assistant in a chat). Stored on `provenance.sources[0].basis_quote` of every fact created, after the same PII layer as the facts (`anonymize` masks it; `block` drops the quote, not the fact, when only the quote carries PII), and returned wherever the fact's provenance is read. It is a citation, not evidence: it never changes the POT Score, the level, corroboration or the endorsement. Blank is the same as omitted.
  - `idempotency_key` string, nullable: Makes this contribution safe to repeat. Within the workspace, a second request with the same key and the same content returns the facts the first one saved (`replayed: true`) instead of creating another copy. The same key for different content, a different POT or a different contributor is a 409 `IDEMPOTENCY_KEY_REUSED`. Send the same value on a timeout retry. The `Idempotency-Key` header is accepted and must match this field when both are set.

## Response `201`

Facts created from contribution

- ContributeResponse: Response for POST /pots/{pot_id}/facts/contribute.
  - `pot_id` string, uuid, required
  - `facts_created` integer, required: Number of facts extracted and saved
  - `fact_ids` string[]: IDs of created facts
  - `facts` ContributedFactState[]: The facts as stored: id, text, POT Score and level. Present on the first response and on an idempotent replay, so a timeout retry can confirm the outcome without listing the POT.
    - `fact_id` string, required
    - `content` string, required
    - `pot_score` number, required
    - `level` string, required
    - `indexed` boolean: True when this response's attempt upserted the fact's embedding.
  - `avg_pot_score` number, required: Average POT Score of created facts
  - `expertise_evaluation` ExpertiseEvaluation, required: Result of evaluating a contributor's expertise relative to a fact's domain.
    - `relevance_score` number, required: How relevant the contributor's expertise is (0.0-1.0)
    - `method` string, required: Evaluation method used: 'heuristic' or 'llm'
    - `matched_areas` string[]: Expertise areas that matched the fact domain
    - `reasoning` string, nullable: LLM reasoning for the evaluation (if method=llm)
  - `edges_created` integer: Edges created by post-ingestion. 0 when `post_ingestion` is `scheduled`: the check runs after this response and a replay shows the counts.
  - `scores_updated` integer: Scores updated by propagation. 0 when `post_ingestion` is `scheduled`: the check runs after this response and a replay shows the counts.
  - `replayed` boolean: True when this is the outcome of an earlier request with the same idempotency key. Nothing new was saved.
  - `partial` boolean: True when this replay names fewer facts than the attempt stored: the rest were never saved, or were deleted since. `facts_missing` is how many.
  - `facts_missing` integer: How many fact ids from the original attempt this response does not include. 0 on a first success and on a complete replay.
  - `post_ingestion` 'skipped' | 'scheduled' | 'completed' | 'failed': `skipped` when edge detection was not asked for. `scheduled` when it is running after this response (the facts above are already stored). `completed` or `failed` on a replay once that run has finished.

## Other responses

- `403`: `PII_NOT_ENTITLED`: the deployment's default PII mode is on and the workspace (or its integrator) is not entitled to the layer
- `404`: POT not found
- `409`: `CONTRIBUTION_IN_FLIGHT` (Retry-After): this idempotency key is still being saved. `IDEMPOTENCY_KEY_REUSED`: the key was already used for a different contribution.
- `422`: Validation error
- `429`: `TOKEN_QUOTA_EXCEEDED`: the monthly tokens are spent
- `502`: LLM service error

## Changes

- **2026-10-23** `92818f8b7a8c`: 1 info
  - added the new optional request property `basis_quote`
- **2026-10-14** `34be7df24641`: 1 info
  - added the new optional request property `valid_from`
- **2026-10-12** `4ec43ee51b64`: 8 info
  - added the new optional `header` request parameter `Idempotency-Key`
  - added the new optional request property `idempotency_key`
  - added the non-success response with the status `409`
  - added the optional property `facts` to the response with the `201` status
  - …4 more
- **2026-09-28** `8da65b524a16`: 2 info
  - added the non-success response with the status `403`
  - added the non-success response with the status `429`
- **2026-09-26** `9c8cf6ae7c8b`: 1 info
  - added the new optional request property `tentative`

[Operation history](https://skmtc.dev/scipot/apis/scipot-core-api/changes/pots/:pot_id/facts/contribute/post.md)

---

[API](https://skmtc.dev/scipot/apis/scipot-core-api.md) - [All operations](https://skmtc.dev/scipot/apis/scipot-core-api/llms.txt) - [OpenAPI document](https://skmtc.dev/scipot/apis/scipot-core-api/revisions/7620af82156a?raw)
