Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / docs/openapi.yaml |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 43d56387270ef4da4cabd794d97ca6a49909bc7d |
| Branch | w3-ai-staffing-recommendations |
| Path or archive member | docs/openapi.yaml |
| Original SHA-256 | 75735bc65d687b163382c6807c11bc549140a53470807579bef23f4eabfcbc20 |
| Git blob SHA | 50c314d204870cd60135d4caef91a1fb607c16fb |
| Reading-copy SHA-256 | 75735bc65d687b163382c6807c11bc549140a53470807579bef23f4eabfcbc20 |
Recovered source transcript
Download complete source text ↓1,064 lines · 33.6 KB
openapi: 3.1.0
info:
title: missionMATCH API
version: "1.0"
description: >
Cleared Workforce and Proposal Staffing Intelligence platform API. Every /api route is multi tenant
(Postgres RLS scoped to the validated session org), behind a first party opaque server side session
plus a signed double submit CSRF token, with per route role based access control and an append only
audit trail. Scores come from one deterministic decimal engine. Clearance is always a self reported
indicator, never platform verified. This document is the API contract surface; the rich intelligence
view bodies are summarized as objects rather than fully expanded.
servers:
- url: https://screenmatch.thatwebhostingguy.com
description: pilot
- url: http://localhost:8080
description: local
tags:
- name: health
- name: auth
- name: candidates
- name: requirements
- name: matching
- name: slates
- name: missioncheck
- name: commandcenter
- name: market
- name: sourcing
- name: users
- name: compliance
- name: staffing
components:
securitySchemes:
cookieSession:
type: apiKey
in: cookie
name: __Host-mm_session
description: Opaque server side session, two stage (pre MFA then active), HttpOnly Secure SameSite Strict.
csrfToken:
type: apiKey
in: header
name: X-CSRF-Token
description: Signed double submit token from GET /api/auth/csrf, required on every unsafe verb.
schemas:
Error:
type: object
properties:
error:
type: string
CandidateListView:
type: object
properties:
id:
type: string
format: uuid
full_name:
type: string
clearance_level:
type: string
description: DB code unknown none confidential secret top_secret or ts_sci
clearance_status:
type: string
years_experience:
type: integer
location:
type: string
available:
type: boolean
RequirementListView:
type: object
properties:
id:
type: string
format: uuid
title:
type: string
required_level:
type: string
customer_agency:
type: string
key_personnel:
type: boolean
MatchResultView:
type: object
description: The explainable score (decimal values as fixed strings) plus the per component trace.
properties:
candidate_id:
type: string
requirement_id:
type: string
total_score:
type: string
example: "92.00"
band:
type: string
enum: [green, yellow, red]
explanation:
type: string
gap_summary:
type: array
items:
type: string
ScreenView:
type: object
description: >
Full screenMATCH intelligence. Contains match, mission_readiness, battle_card,
clearance_intelligence, contract_readiness, win_impact, and next_steps.
MissionCheckRequest:
type: object
properties:
resume_text:
type: string
description: Resume text extracted in the browser. The file is never uploaded.
job_description_text:
type: string
description: Mode B only.
limit:
type: integer
description: Mode A only.
SaveCandidateRequest:
type: object
required: [full_name]
properties:
full_name:
type: string
clearance_level:
type: string
clearance_poly:
type: string
clearance_status:
type: string
years_experience:
type: integer
location:
type: string
skills:
type: array
items:
type: string
certifications:
type: array
items:
type: string
customer_experience:
type: array
items:
type: string
LoginRequest:
type: object
required: [email, password]
properties:
email:
type: string
format: email
password:
type: string
StaffingError:
type: object
description: The one typed error envelope for the W3 staffing endpoints.
properties:
error:
type: object
required: [code, message]
properties:
code:
type: string
description: validation | not_found | conflict | problem_too_large | solve_timeout | no_database | no_tenant | bad_request | internal
message: { type: string }
fields:
type: object
additionalProperties: { type: string }
description: Present on a 422; per field validation messages.
PortfolioCounterfactual:
type: object
description: The per assignment explanation computed from the value matrix, not from solver internals.
properties:
assigned_total: { type: string, description: The assigned candidate engine total for this requirement (0 to 100). }
candidate_next_requirement: { type: string, format: uuid, description: The candidate's next best alternative requirement. }
candidate_value_delta: { type: integer, format: int64, description: Scaled value lost by not placing them on their next best requirement. }
requirement_next_candidate: { type: string, format: uuid, description: The requirement's next best candidate if this one were removed. }
requirement_value_delta: { type: integer, format: int64, description: Scaled value drop to that next best candidate. }
RecommendationEvidence:
type: object
description: Structured justification; only the fields relevant to the action are present.
properties:
engine_total: { type: string }
band: { type: string, enum: [green, yellow, red] }
reskill:
type: object
properties:
held_skill: { type: string }
missing_skill: { type: string }
distance: { type: string, description: Cosine distance between the held and the missing skill. }
reassign:
type: object
properties:
from_requirement_id: { type: string, format: uuid }
priority_delta: { type: string }
counterfactual: { $ref: '#/components/schemas/PortfolioCounterfactual' }
open_slots: { type: integer, description: On a source recommendation, the slots with no qualified, available bench. }
trigger:
type: object
properties:
kind: { type: string, description: availability | certification | clearance | new_opportunity | recompute }
detected_at: { type: string, format: date-time }
faith_fallback_reason:
type: string
description: >
Why the faithfulness gate did not confirm the prose (could_not_confirm, plane_timeout, circuit_open,
generation_disabled, proactive_batch, eeo_guard). Recorded for narration tuning; never in an audit payload.
StaffingRecommendation:
type: object
description: One recommended staffing action. The decision columns mirror slate_members.
required: [id, run_id, requirement_id, action, rank, confidence, rationale, rationale_source, status]
properties:
id: { type: string, format: uuid }
run_id: { type: string, format: uuid }
requirement_id: { type: string, format: uuid }
candidate_id: { type: string, format: uuid, description: Absent for a source recommendation, which names no candidate. }
action: { type: string, enum: [assign, reassign, reskill, source, team] }
rank: { type: integer }
confidence: { type: string, description: Deterministic transparency aid in [0,1], never a probability and never a decision input. }
value_score: { type: integer, format: int64, description: Scaled optimizer objective value; portfolio runs only. }
rationale: { type: string }
rationale_source: { type: string, enum: [ai_faithful, deterministic] }
evidence: { $ref: '#/components/schemas/RecommendationEvidence' }
status: { type: string, enum: [pending, approved, rejected, dismissed, superseded] }
decided_by: { type: string, format: uuid }
decided_at: { type: string, format: date-time }
decision_note: { type: string }
created_at: { type: string, format: date-time }
StaffingRun:
type: object
required: [id, view, model_version, parameters, created_at]
properties:
id: { type: string, format: uuid }
view: { type: string, enum: [gap, portfolio, proactive] }
scope_ref: { type: string, format: uuid, description: Requirement id for a gap run, candidate id for a proactive run, absent for portfolio. }
model_version: { type: string }
parameters: { type: object, description: The staffing floor, adjacency threshold, value reference, scale, and value source actually used. }
created_at: { type: string, format: date-time }
StaffingRunResponse:
type: object
required: [run, recommendations]
properties:
run: { $ref: '#/components/schemas/StaffingRun' }
recommendations:
type: array
items: { $ref: '#/components/schemas/StaffingRecommendation' }
ProactiveResponse:
type: object
properties:
recommendations:
type: array
items: { $ref: '#/components/schemas/StaffingRecommendation' }
limit: { type: integer }
offset: { type: integer }
GapRequest:
type: object
description: Optional per run overrides; the values used are recorded in the run parameters.
properties:
staffing_floor: { type: string, description: "decimal in [0,100]; default 75, the Yellow band floor" }
adjacency_threshold: { type: string, description: "decimal cosine distance in [0,2]; default 0.40" }
PortfolioRequest:
type: object
properties:
requirement_ids:
type: array
items: { type: string, format: uuid }
description: Optional explicit subset of open requirements; when omitted, ALL open requirements are used.
staffing_floor: { type: string }
adjacency_threshold: { type: string }
DecisionRequest:
type: object
required: [decision]
properties:
decision: { type: string, enum: [approved, rejected, dismissed] }
note: { type: string }
responses:
Forbidden:
description: The session role lacks the required capability, or no validated tenant.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- cookieSession: []
csrfToken: []
paths:
/healthz:
get:
tags: [health]
summary: Liveness. Never touches the database.
security: []
responses:
'200':
description: ok
/readyz:
get:
tags: [health]
summary: Readiness. Pings the app and compliance pools.
security: []
responses:
'200':
description: ready
'503':
description: a pool is not ready
/healthz/fips:
get:
tags: [health]
summary: FIPS module status.
security: []
responses:
'200':
description: module info
/metrics:
get:
tags: [health]
summary: Prometheus metrics. Operational, no PII.
security: []
responses:
'200':
description: exposition text
/api/auth/csrf:
get:
tags: [auth]
summary: Issue the CSRF double submit token and set the companion cookie.
security: []
responses:
'200':
description: the csrf_token
/api/auth/login:
post:
tags: [auth]
summary: Password login stage 1. Returns a pre MFA session. MFA is mandatory before an active session.
security:
- csrfToken: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: pre MFA session established
'401':
description: bad credentials
'429':
description: rate limited
/api/auth/mfa/enroll:
post:
tags: [auth]
summary: Begin TOTP enrollment. Returns the otpauth uri and recovery codes.
security:
- csrfToken: []
responses:
'200':
description: enrollment material
/api/auth/mfa/activate:
post:
tags: [auth]
summary: Activate TOTP by confirming a code during enrollment.
security:
- csrfToken: []
responses:
'200':
description: activated
/api/auth/mfa/verify:
post:
tags: [auth]
summary: Verify a TOTP code to upgrade a pre MFA session to active.
security:
- csrfToken: []
responses:
'200':
description: active session
'401':
description: bad code
/api/auth/mfa/recover:
post:
tags: [auth]
summary: Use a single use recovery code in place of TOTP.
security:
- csrfToken: []
responses:
'200':
description: active session
/api/auth/invite/accept:
post:
tags: [auth]
summary: Accept an invite. Set the password and enroll MFA.
security:
- csrfToken: []
responses:
'200':
description: accepted
/api/auth/session:
get:
tags: [auth]
summary: The current validated principal (org, user, role).
responses:
'200':
description: session principal
'403':
$ref: '#/components/responses/Forbidden'
/api/auth/logout:
post:
tags: [auth]
summary: Revoke the current session.
responses:
'200':
description: logged out
/api/auth/logout-all:
post:
tags: [auth]
summary: Revoke every session for the current user.
responses:
'200':
description: done
/api/auth/password/change:
post:
tags: [auth]
summary: Change the password for the current user.
responses:
'200':
description: changed
/api/candidates:
get:
tags: [candidates]
summary: List candidates. Capability candidate.read.
parameters:
- name: limit
in: query
schema:
type: integer
- name: offset
in: query
schema:
type: integer
responses:
'200':
description: candidate list
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/CandidateListView'
'403':
$ref: '#/components/responses/Forbidden'
post:
tags: [candidates]
summary: Create a candidate. Capability candidate.write, audited. Clearance is self reported.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SaveCandidateRequest'
responses:
'201':
description: the new id
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/Forbidden'
/api/candidates/{candidateID}:
get:
tags: [candidates]
summary: One candidate by id. Capability candidate.read. Missing or cross org both return 404.
parameters:
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: candidate
content:
application/json:
schema:
$ref: '#/components/schemas/CandidateListView'
'404':
description: not found
/api/candidates/intelligence:
get:
tags: [candidates]
summary: Workforce intelligence view. Scores the pool. Capability search.run, audited served scores.
responses:
'200':
description: candidate intelligence list
/api/candidates/{candidateID}/opportunities:
get:
tags: [candidates]
summary: Best fit open opportunities for one candidate. Capability search.run.
parameters:
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: ranked opportunity fits
/api/candidates/{candidateID}/clearance-provenance:
patch:
tags: [candidates]
summary: Record how a clearance is known. Capability candidate.write, audited. The platform never verifies a clearance.
parameters:
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
requestBody:
content:
application/json:
schema:
type: object
properties:
provenance:
type: string
enum: [self_reported, sponsor_verified, document, continuous_vetting]
verified_at:
type: string
format: date
responses:
'200':
description: updated
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: not found
/api/candidates/{candidateID}/notice:
post:
tags: [compliance]
summary: Record an AEDT candidate notice and disclosure. Capability candidate.write, audited.
parameters:
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: recorded
/api/requirements:
get:
tags: [requirements]
summary: List requirements. Capability candidate.read.
responses:
'200':
description: requirement list
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/RequirementListView'
/api/requirements/{requirementID}:
get:
tags: [requirements]
summary: One requirement by id. Capability candidate.read.
parameters:
- name: requirementID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: requirement
content:
application/json:
schema:
$ref: '#/components/schemas/RequirementListView'
'404':
description: not found
/api/requirements/{requirementID}/candidates:
get:
tags: [matching]
summary: Rank every candidate against one requirement. Capability search.run, audited served scores.
parameters:
- name: requirementID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: ranked candidates
/api/match:
post:
tags: [matching]
summary: screenMATCH score one candidate vs one requirement. Capability search.run, audited.
requestBody:
content:
application/json:
schema:
type: object
properties:
candidate_id:
type: string
requirement_id:
type: string
responses:
'200':
description: match result
content:
application/json:
schema:
$ref: '#/components/schemas/MatchResultView'
/api/screen:
post:
tags: [matching]
summary: Full screening intelligence for one candidate vs one requirement. Capability search.run, audited.
requestBody:
content:
application/json:
schema:
type: object
properties:
candidate_id:
type: string
requirement_id:
type: string
responses:
'200':
description: screen view
content:
application/json:
schema:
$ref: '#/components/schemas/ScreenView'
/api/slates/generate:
post:
tags: [slates]
summary: Generate and freeze a candidate slate. Capability slate.write, audited.
responses:
'201':
description: slate result
/api/slates/{slateID}/members/{candidateID}/decision:
patch:
tags: [slates]
summary: Human in the loop approve or reject a slate member. Capability slate.write, audited. Never auto rejects.
parameters:
- name: slateID
in: path
required: true
schema:
type: string
format: uuid
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
requestBody:
content:
application/json:
schema:
type: object
properties:
decision:
type: string
enum: [approved, rejected]
responses:
'200':
description: recorded
/api/slates/{slateID}/members/{candidateID}/trace:
get:
tags: [slates]
summary: The frozen decision trace for a slate member. Capability candidate.read.
parameters:
- name: slateID
in: path
required: true
schema:
type: string
format: uuid
- name: candidateID
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: decision trace
/api/mission-check/opportunities:
post:
tags: [missioncheck]
summary: Mode A. Score an in memory resume against every open opportunity. Capability search.run, ephemeral audit, stores nothing.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MissionCheckRequest'
responses:
'200':
description: ranked rows plus the parsed candidate, assumptions, and data categories
'400':
$ref: '#/components/responses/BadRequest'
/api/mission-check/screen:
post:
tags: [missioncheck]
summary: Mode B. Score an in memory resume against a pasted job description. Capability search.run, ephemeral audit, stores nothing.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MissionCheckRequest'
responses:
'200':
description: screen view, recommendation, parsed candidate and requirement
'400':
$ref: '#/components/responses/BadRequest'
/api/command-center:
get:
tags: [commandcenter]
summary: Workforce Command Center aggregate. Readiness, gaps, Revenue at Risk. Capability candidate.read.
responses:
'200':
description: command center view
/api/opportunities:
get:
tags: [market]
summary: Cached SAM.gov opportunities, the requirement feed.
responses:
'200':
description: opportunities
/api/opportunities/{noticeID}:
get:
tags: [market]
summary: One cached SAM.gov opportunity.
parameters:
- name: noticeID
in: path
required: true
schema:
type: string
responses:
'200':
description: opportunity
'404':
description: not found
/api/awards:
get:
tags: [market]
summary: Cached USASpending incumbent awards by NAICS.
responses:
'200':
description: awards
/api/awards/galapagos:
get:
tags: [market]
summary: Cached Galapagos past performance awards.
responses:
'200':
description: awards
/api/wages:
get:
tags: [market]
summary: Cached BLS OEWS regional wage baseline.
responses:
'200':
description: wages
/api/sourcing/candidates:
get:
tags: [sourcing]
summary: Externally sourced candidates. Capability sourcing.read.
responses:
'200':
description: sourced candidates
/api/sourcing/log:
get:
tags: [sourcing]
summary: The sourcing provenance and legal basis log. Capability sourcing.read.
responses:
'200':
description: sourcing log
/api/sourcing/candidates/{id}:
delete:
tags: [sourcing]
summary: Hard delete a sourced person and their PII, right to erasure. Capability privacy.erase, audited.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: erased
/api/users:
get:
tags: [users]
summary: List org users. Capability user.invite.
responses:
'200':
description: user summaries
/api/users/invite:
post:
tags: [users]
summary: Invite a user with an authority bounded role. Capability user.invite, audited. The invite link is returned for manual delivery.
responses:
'200':
description: invite link
/api/users/{id}/role:
patch:
tags: [users]
summary: Change a user role, strictly below the actor role. Capability user.role.change, audited.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: changed
/api/users/{id}/status:
patch:
tags: [users]
summary: Enable or disable a user. Capability user.disable, audited.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: changed
/api/users/{id}/mfa/reset:
post:
tags: [users]
summary: Reset a user MFA enrollment. Capability user.disable, audited.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: reset
/api/users/{id}/recovery:
post:
tags: [users]
summary: Reissue recovery material for a user. Capability user.invite, audited.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: recovery link
/api/aedt/model-versions:
get:
tags: [compliance]
summary: The AEDT model version registry. Capability compliance.read.
responses:
'200':
description: model versions
/api/aedt/inventory:
get:
tags: [compliance]
summary: The AEDT tool inventory. Capability compliance.read.
responses:
'200':
description: inventory
post:
tags: [compliance]
summary: Upsert an AEDT inventory entry. Capability compliance.read, audited.
responses:
'200':
description: upserted
/api/compliance/retention/run:
post:
tags: [compliance]
summary: Run the retention sweep, anonymize in place past the window. Capability compliance.read, audited.
responses:
'200':
description: swept
/api/compliance/adverse-impact:
get:
tags: [compliance]
summary: The four fifths adverse impact report. Compliance role only.
responses:
'200':
description: report
'503':
description: compliance pool not configured
/api/compliance/adverse-impact/export:
get:
tags: [compliance]
summary: Publication ready adverse impact export. Compliance role only.
responses:
'200':
description: export text
/api/audit:
get:
tags: [compliance]
summary: The append only audit trail for the org. Capability audit.read.
parameters:
- name: limit
in: query
schema:
type: integer
- name: offset
in: query
schema:
type: integer
responses:
'200':
description: audit log
/recommendations/gap/{requirementID}:
post:
tags: [staffing]
summary: >
Generate the ranked recommendations to close one named gap. Capability staffing.recommend (a
write: it persists a fresh run, superseding prior pending gap rows for this requirement). Produces
ASSIGN, REASSIGN, RESKILL, and a single SOURCE note; never auto rejects; the engine is the floor.
parameters:
- name: requirementID
in: path
required: true
schema: { type: string, format: uuid }
requestBody:
required: false
content:
application/json:
schema: { $ref: '#/components/schemas/GapRequest' }
responses:
'200':
description: The run and its recommendations in rank order.
content:
application/json:
schema: { $ref: '#/components/schemas/StaffingRunResponse' }
'403': { $ref: '#/components/responses/Forbidden' }
'404':
description: The requirement is not a real, open gap in this tenant.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
'422':
description: Invalid per run override (floor or adjacency out of range).
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
/recommendations/portfolio:
post:
tags: [staffing]
summary: >
Optimize the whole available bench across the open requirements (a pure Go minimum cost flow that
maximizes total value). Capability staffing.recommend. Each ASSIGN carries the scaled value and the
value matrix counterfactual; unfilled slots become SOURCE. Two runs for one org serialize on an
advisory lock. An empty bench or no open requirement returns an empty allocation (200), not an error.
requestBody:
required: false
content:
application/json:
schema: { $ref: '#/components/schemas/PortfolioRequest' }
responses:
'200':
description: The run and the optimal allocation with per assignment explanations.
content:
application/json:
schema: { $ref: '#/components/schemas/StaffingRunResponse' }
'403': { $ref: '#/components/responses/Forbidden' }
'413':
description: The candidate by requirement problem exceeds the configured size cap.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
'422':
description: An unknown requirement id in the explicit subset, or an invalid override.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
'503':
description: The portfolio solve exceeded its time budget.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
/recommendations/proactive:
get:
tags: [staffing]
summary: >
The current bench watch: the pending proactive recommendations the scheduled recompute wrote, per
candidate best fits. Capability candidate.read. Capped and paginated.
parameters:
- name: limit
in: query
schema: { type: integer, description: Capped at the server read page maximum (default 200). }
- name: offset
in: query
schema: { type: integer }
responses:
'200':
description: The current bench watch.
content:
application/json:
schema: { $ref: '#/components/schemas/ProactiveResponse' }
'403': { $ref: '#/components/responses/Forbidden' }
/recommendations/{recommendationID}/decision:
post:
tags: [staffing]
summary: >
Approve, reject, or dismiss one recommendation (human in the loop). Capability slate.write. The row
is locked; a decision on a non pending row (already decided, or concurrently superseded) returns 409.
Rejecting dismisses only that suggestion; it never blacklists a candidate, and nothing is automatic.
parameters:
- name: recommendationID
in: path
required: true
schema: { type: string, format: uuid }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/DecisionRequest' }
responses:
'200':
description: The decided recommendation.
content:
application/json:
schema: { $ref: '#/components/schemas/StaffingRecommendation' }
'403': { $ref: '#/components/responses/Forbidden' }
'404':
description: No such recommendation in this tenant.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
'409':
description: The recommendation was already decided or superseded.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }
'422':
description: The decision is not one of approved, rejected, dismissed.
content: { application/json: { schema: { $ref: '#/components/schemas/StaffingError' } } }