> ## Documentation Index
> Fetch the complete documentation index at: https://docs.droyd.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/competitions/{competition_id_or_slug}/submissions/create

> Requires competition lifecycle state live. Practice mode keeps skills, datasets, and evaluations available but returns 409 competition_submissions_closed from this route.

Submit an uploaded agent artifact to a competition.

Auth: Droyd API key or signed-in user bearer token. Canonical route:
`POST /v1/competitions/{competition_id_or_slug}/submissions/create`.
Aliases:
`POST /api/v1/competitions/{competition_id_or_slug}/submissions/create`,
`POST /v1/competitions/{competition_id_or_slug}/submissions`, and
`POST /api/v1/competitions/{competition_id_or_slug}/submissions`. Body requires
`experiment_id`, `agent_version_id`, and `artifact_id`. `client_key` is
user-scoped idempotency. `workflow_node_id` links the submission back to a
workflow node.

The competition lifecycle must be `live`. A competition in `practice` exposes
its skill, datasets, and evaluations but rejects this route with
`409 competition_submissions_closed` until race mode opens.

Droyd-hosted competitions enqueue a hosted evaluation and return
`evaluation_id`, `job_id`, and `poll_url`. External provider competitions may
return those fields as null/omitted and include `external_submission` plus
`provider_submission`. For ORO, `poll_url` points to the live provider-status
adapter and `poll_after_ms` is 15000. Follow that URL until the returned
`status.terminal` is true; do not poll the released submission-detail route.

For first-party Droyd race competitions, pass the caller-owned Solana devnet
`wallet_id`. Pass the optional top-level `race_id` to choose a race; a
`race_id` inside `metadata` is never used for selection. If top-level
`race_id` is omitted, submission succeeds only when exactly one configured
race is currently open. Zero open races return `409 no_open_race`; multiple
open races return `409 ambiguous_open_race`. The API atomically creates or
replays the shared submission, race entry, and embedded-wallet SPL payment
intent. Race responses include typed `entry`, `payment_intent`, and `payment`
instructions. `payment.type` is `spl_transfer` for legacy races and
`race_entry` for protocol v3. The v3 shape binds the released program, race
PDA, entry UUID, and canonical submission-manifest hash; it does not use a
memo. Compatible race-scoped `client_key` retries return the original ledger
with HTTP 200; changed requests return `409 idempotency_conflict`.

For ORO, pass `wallet_id` for the caller-owned registered local Bittensor
wallet and a passed `preflight_id` from `POST /v1/competitions/oro/preflights`.
The preflight must include provider checks, match the same wallet and artifact,
contain all required ORO checks, and be no more than five minutes old.
The request must include locally signed ORO headers `X-Hotkey`, `X-Timestamp`,
`X-Nonce`, and `X-Signature`; the API verifies that `X-Hotkey` matches the
selected wallet and forwards the signed provider request to ORO. Use a new
signature and nonce; the preflight signature cannot be replayed. `agent_name`
is optional and defaults to the experiment title when possible. Before
forwarding, Droyd verifies that the current `agent.py` bytes still match the
SHA-256 recorded when the artifact upload completed. Hosted
Bittensor wallet signing currently returns `501 hosted_bittensor_submission_todo`.

For NOVA Blueprint, pass the same candidate and local Bittensor `wallet_id`
used by a passed provider preflight, plus a required `client_key`. The CLI
builds the deterministic `submission/` tar.gz and sends only `X-Hotkey`,
`X-Code-Hash`, and `X-Signature`; the API rebuilds the archive from the stored
artifact and rejects any hash/signature mismatch. Pass
`X-Nova-Overwrite-Confirmed: true` because MetaNova has one mutable epoch slot.
Also pass `X-Nova-Charge-Confirmed: true` unless the current preflight quote is
explicitly free. A provider timeout, reset, HTTP 408, or 5xx returns HTTP 202
with `status="outcome_unknown"`,
`outcome.provider_retry_safe=false`, and
`outcome.client_replay_safe=true`. Retry only with the identical `client_key`
to read the durable Droyd receipt; never create a fresh submission. Nova
returns `poll_url=null` while asynchronous provider-result reconciliation is
pending. If the AWS ingestion registration fails after MetaNova accepts, the
API returns a registration-specific error but preserves the accepted receipt;
repeat the identical request/client key to retry only registration.


## OpenAPI

````yaml api-key.openapi.json POST /v1/competitions/{competition_id_or_slug}/submissions/create
openapi: 3.1.0
info:
  title: Droyd API-key Automation API
  version: 1.0.0
  description: >-
    The production API contract for Droyd API-key automations. It contains only
    operations that accept a Droyd API key. `/v1` is canonical; `/api/v1` is a
    compatibility alias.
servers: []
security:
  - droydApiKeyBearer: []
  - droydApiKeyHeader: []
tags:
  - name: Identity
    description: Account-scoped automation identity and onboarding state.
  - name: Workflows
    description: Caller-owned workflows and research nodes.
  - name: Competitions
    description: Competition readiness, submissions, and race entries.
  - name: Experiments
    description: Experiments, artifacts, agent versions, and evaluations.
  - name: Usage
    description: Caller-owned evaluation usage.
  - name: Billing
    description: Caller-owned prepaid credit balances and funding.
paths:
  /v1/competitions/{competition_id_or_slug}/submissions/create:
    post:
      tags:
        - Competitions
      summary: Submit an uploaded agent artifact to a live competition.
      description: >-
        Requires competition lifecycle state live. Practice mode keeps skills,
        datasets, and evaluations available but returns 409
        competition_submissions_closed from this route.
      operationId: createCompetitionSubmission
      parameters:
        - $ref: '#/components/parameters/CompetitionIdOrSlug'
      requestBody:
        $ref: '#/components/requestBodies/CompetitionSubmission'
      responses:
        '200':
          $ref: '#/components/responses/Success'
        '201':
          $ref: '#/components/responses/Success'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  parameters:
    CompetitionIdOrSlug:
      name: competition_id_or_slug
      in: path
      required: true
      schema:
        type: string
        minLength: 1
  requestBodies:
    CompetitionSubmission:
      required: true
      content:
        application/json:
          schema:
            type: object
            required:
              - experiment_id
              - agent_version_id
              - artifact_id
            properties:
              experiment_id:
                type: string
                format: uuid
              agent_version_id:
                type: string
                format: uuid
              artifact_id:
                type: string
                format: uuid
              race_id:
                type: string
                format: uuid
              wallet_id:
                type: string
                format: uuid
              preflight_id:
                type: string
                format: uuid
              workflow_node_id:
                type: string
                format: uuid
              client_key:
                type: string
              metadata:
                type: object
                additionalProperties: true
  responses:
    Success:
      description: Successful response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SuccessEnvelope'
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Forbidden:
      description: Account state or route policy prevents the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: The resource is missing or not owned by the API-key account.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Conflict:
      description: The resource state or idempotency key conflicts with the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ServerError:
      description: >-
        The request could not be completed. Retry only when the operation's
        idempotency contract permits it.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  schemas:
    SuccessEnvelope:
      type: object
      required:
        - ok
      properties:
        ok:
          const: true
      additionalProperties: true
    ErrorEnvelope:
      type: object
      required:
        - ok
        - error
      properties:
        ok:
          const: false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
  securitySchemes:
    droydApiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: Droyd API key
      description: A Droyd API key in the Authorization Bearer slot.
    droydApiKeyHeader:
      type: apiKey
      in: header
      name: x-droyd-api-key
      description: A Droyd API key when Authorization is absent.

````