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

# CSVBox import webhook (called by CSVBox, not the client)

> The CSVBox sheet's API destination, with Server Side Validation on. CSVBox posts the submitted rows here; this one call both validates and accepts. Sheet setup: chunk size of at least 1000 (the whole file in one request), Results Page set to not close on import complete, and resubmission of All Rows, so each row keeps its `row_number`.
     *
     * - Any error: HTTP **211** with the errors as CSVBox cell errors (`row_id` = CSVBox `row_number`, `column` = the sheet column). CSVBox shows the fail screen and takes the agent back to its grid to correct and resubmit. Nothing is written: no import, no athlete, no notification.
     * - No error: the rows are persisted as the session's import and queued, then HTTP 200. A 200 means only that MOGL accepted the batch; completion is the import's own terminal status.
     * - A repeated delivery under a session that was already accepted returns 200 with the same import and creates nothing.
     *
     * Required, matching athlete signup: `first_name`, `last_name` (max 255), `email`, `phone` (10 digits, the athlete's own), `sport` (name) or `sport_category_id`, `school` (name) or `university_id`, `graduation_year` (4 digits), `dob`, `gender` (Male, Female or Prefer not to say), `us_citizen` (Y/N). Optional: `hometown`, `jersey_no` (max 10), `height`, `weight`, `bio` (max 5000), `profile_photo_url`. Other keys are ignored.
     *
     * A bad required field is an error on its cell. A bad optional field never is: the file is accepted, that field is left empty, and the row's `message` in the results says which fields were skipped and why (CSVBox has no warnings).
     *
     * `profile_photo_url` (http or https) is downloaded in the background after the athlete is added: JPEG, PNG or WebP, up to 25 MB, public addresses only. If it cannot be used, the athlete keeps no photo and the reason is added to the row's `message`. A photo the athlete already has is never replaced.
     *
     * An email or phone that already belongs to a MOGL account is an error, never a link, with athlete signup's own messages on the field that is taken: `Email already exists` (email) and `This phone number is already being used by an existing user` (phone). The agent invites existing athletes by email instead. A shared test phone is refused. An athlete already on this agent's roster is not an error: they are not duplicated, only their empty fields are filled. An email or phone that repeats within the file is an error on every occurrence. A sport or school that does not match one of MOGL's by name is an error on that cell: `The sport "X" was not found.` or `The school "X" was not found.`
     *
     * Every rule is checked again when each row is provisioned, since an account can change in between; such a row fails in the results and can be retried. More than 1000 rows, or a file that arrives in parts, is a table error. Rate limit 60/min.



## OpenAPI

````yaml /storage/api-docs/api-docs.json post /agent/roster-imports/csvbox
openapi: 3.0.0
info:
  title: MOGL Core API
  description: >-
    MOGL Platform Core API — powering athlete NIL deals, brand partnerships,
    agent management, payments, messaging, and more.
     *
     * ## Authentication
     * - **JWT Bearer Token**: Most endpoints require a Bearer token obtained via `/api/login`.
     * - **Server-to-Server API Key**: Internal/lambda/cron endpoints use an `X-API-KEY` header.
     *
     * ## Rate Limiting
     * - Registration & login endpoints: 4–15 requests per minute
     * - Public/influencer endpoints: 200 requests per minute
     * - Authenticated endpoints: standard Laravel throttle
  version: 1.0.0
servers:
  - url: http://localhost/mogl/mogl-backend/api
    description: Local
  - url: https://dev-api.mogl.online/api
    description: Dev
  - url: https://staging-api.mogl.online/api
    description: Staging
  - url: https://api.mogl.online/api
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: Login, registration, password reset
  - name: User Registration - Common
    description: Common user registration & email check
  - name: User Registration - Athlete
    description: Athlete registration & onboarding steps
  - name: User Registration - Partner
    description: Partner/Brand registration & onboarding steps
  - name: User Registration - Agent
    description: Agent registration & onboarding steps
  - name: User Registration - Fan
    description: Fan registration & onboarding steps
  - name: User Profile
    description: User profile management, settings, social media
  - name: Athlete
    description: 'Athlete-specific endpoints: search, details, availability'
  - name: Athlete - Availability Confirmation
    description: Post-hiring availability confirmation workflow
  - name: Athlete - NIL Feed
    description: Athlete NIL feed and partner search
  - name: Athlete - AI Assistant
    description: >-
      AI-powered job assistant for athletes — chat, conversation history, read
      state, and clarification drafts
  - name: Athlete - AI Waitlist
    description: AI Athlete Assistant waitlist status, social proof, and joining
  - name: Partner
    description: Partner/Brand-specific endpoints
  - name: Partner - External Job
    description: External job link & applicant management
  - name: Partner - Deliverable Report
    description: Deliverable detail reports for brands
  - name: Partner - Screening Questions
    description: Screening question reports
  - name: Partner - Contract Management
    description: Brand contract management with athletes
  - name: Partner - Content Library
    description: Brand content library for deliverable assets
  - name: Agent
    description: Agent profile, athlete management, contracts
  - name: Agent - Athletes
    description: Agent-athlete relationship management
  - name: Agent - Stripe
    description: Agent payment methods & billing via Stripe
  - name: Agent - Pay
    description: 'Agent-wide payout feed: money MOGL sends the agent through Stripe Express'
  - name: Agent - Contracts
    description: Agent athlete contract management
  - name: Agent - Availability Confirmation
    description: Agent managing athlete job availability
  - name: Agent - Roster Import
    description: >-
      CSV client list import through CSVBox (ENG-1619 / ENG-1702): session,
      CSVBox webhook, progress, row results, retry
  - name: Agent - Roster Pages
    description: >-
      ENG-1685: My Roster Link, public roster directory, public roster page, and
      roster SEO URL resolution. A roster is the agent's current linked
      athletes; nothing is stored separately.
  - name: Jobs
    description: Job/deal CRUD, search, invitations, hiring, deliverables
  - name: Deals
    description: Deal listing, my deals, bulk operations
  - name: Deal Folder
    description: >-
      Deal folder file workflow — uploads, replacements, approvals, revisions,
      comments. Accessible by athlete, partner/brand, agent, and iOS clients
      (per-action authorisation enforced server-side).
  - name: Deal Folder iOS API
    description: >-
      iOS-only direct-to-S3 multipart upload endpoints for deal folder files
      (initiate/complete for new uploads and replacements). Web clients should
      use the standard `Deal Folder` upload/replace endpoints instead.
  - name: Chat
    description: Messaging, chat contacts, notifications
  - name: Notifications
    description: Notification preferences, in-app notifications
  - name: Payments
    description: Stripe payments, payment history, cards, bulk pay
  - name: Subscription
    description: Brand subscription plans & management
  - name: Public Pages
    description: Public content endpoints (no auth required)
  - name: Public - Influencer/SEO
    description: Public influencer discovery, sitemaps, directories
  - name: Services
    description: Athlete service marketplace
  - name: iOS Device
    description: iOS device tokens, live activities
  - name: Onboarding Tutorial
    description: Onboarding tutorial progress
  - name: MOGL Rosters
    description: Roster management & display
  - name: Internal / Cron
    description: Server-to-server and cron job endpoints
  - name: Internal / AI Assistant Tools
    description: >-
      Server-to-server Athlete Assistant tool gateway used by mogl-ai-service.
      Eight Laravel tools share POST /internal/ai-assistant/tools/{tool}.
      search_kb is Python-local and is not in this path enum.
  - name: Internal / QA Scenario Lab
    description: >-
      Feature-flagged DEV/STAGING-only endpoints called by the authorized Back
      Office QA Scenario Lab.
  - name: Affiliate
    description: Athlete affiliate links & tracking
  - name: Internal
    description: Internal
  - name: Public - Social Linking
    description: Public - Social Linking
paths:
  /agent/roster-imports/csvbox:
    post:
      tags:
        - Agent - Roster Import
      summary: CSVBox import webhook (called by CSVBox, not the client)
      description: >-
        The CSVBox sheet's API destination, with Server Side Validation on.
        CSVBox posts the submitted rows here; this one call both validates and
        accepts. Sheet setup: chunk size of at least 1000 (the whole file in one
        request), Results Page set to not close on import complete, and
        resubmission of All Rows, so each row keeps its `row_number`.
             *
             * - Any error: HTTP **211** with the errors as CSVBox cell errors (`row_id` = CSVBox `row_number`, `column` = the sheet column). CSVBox shows the fail screen and takes the agent back to its grid to correct and resubmit. Nothing is written: no import, no athlete, no notification.
             * - No error: the rows are persisted as the session's import and queued, then HTTP 200. A 200 means only that MOGL accepted the batch; completion is the import's own terminal status.
             * - A repeated delivery under a session that was already accepted returns 200 with the same import and creates nothing.
             *
             * Required, matching athlete signup: `first_name`, `last_name` (max 255), `email`, `phone` (10 digits, the athlete's own), `sport` (name) or `sport_category_id`, `school` (name) or `university_id`, `graduation_year` (4 digits), `dob`, `gender` (Male, Female or Prefer not to say), `us_citizen` (Y/N). Optional: `hometown`, `jersey_no` (max 10), `height`, `weight`, `bio` (max 5000), `profile_photo_url`. Other keys are ignored.
             *
             * A bad required field is an error on its cell. A bad optional field never is: the file is accepted, that field is left empty, and the row's `message` in the results says which fields were skipped and why (CSVBox has no warnings).
             *
             * `profile_photo_url` (http or https) is downloaded in the background after the athlete is added: JPEG, PNG or WebP, up to 25 MB, public addresses only. If it cannot be used, the athlete keeps no photo and the reason is added to the row's `message`. A photo the athlete already has is never replaced.
             *
             * An email or phone that already belongs to a MOGL account is an error, never a link, with athlete signup's own messages on the field that is taken: `Email already exists` (email) and `This phone number is already being used by an existing user` (phone). The agent invites existing athletes by email instead. A shared test phone is refused. An athlete already on this agent's roster is not an error: they are not duplicated, only their empty fields are filled. An email or phone that repeats within the file is an error on every occurrence. A sport or school that does not match one of MOGL's by name is an error on that cell: `The sport "X" was not found.` or `The school "X" was not found.`
             *
             * Every rule is checked again when each row is provisioned, since an account can change in between; such a row fails in the results and can be retried. More than 1000 rows, or a file that arrives in parts, is a table error. Rate limit 60/min.
      operationId: agentRosterImportCsvboxWebhook
      requestBody:
        description: 'CSVBox''s JSON delivery: one object per row.'
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                required:
                  - row_number
                  - row_data
                properties:
                  import_id:
                    description: >-
                      CSVBox's import id; new on every resubmission. Not used as
                      the idempotency key.
                    type: integer
                    example: 79418895
                  sheet_id:
                    type: integer
                    example: 55
                  sheet_name:
                    type: string
                    example: MOGL Agent Roster Import - Development
                  row_number:
                    description: Becomes the row's `row_number` in the results.
                    type: integer
                    example: 1
                  total_rows:
                    description: >-
                      Rows in the whole file. Must equal the number of rows in
                      this request, or the file is reported as arriving in
                      parts.
                    type: integer
                    example: 1
                  env_name:
                    type: string
                    example: development
                  original_filename:
                    type: string
                    example: roster.csv
                  custom_fields:
                    description: >-
                      From the importer's setUser; not trusted for
                      authorization.
                    properties:
                      user_id:
                        type: string
                        example: '1002'
                    type: object
                  row_data:
                    properties:
                      first_name:
                        type: string
                        example: Alex
                      last_name:
                        type: string
                        example: Patel
                      email:
                        type: string
                        example: alex@example.edu
                      phone:
                        type: string
                        example: '5551234567'
                      sport:
                        type: string
                        example: Football
                      school:
                        type: string
                        example: University of California
                      graduation_year:
                        type: string
                        example: '2027'
                      dob:
                        type: string
                        example: '2005-04-12'
                      gender:
                        type: string
                        enum:
                          - Male
                          - Female
                          - Prefer not to say
                        example: Male
                      us_citizen:
                        type: string
                        enum:
                          - 'Y'
                          - 'N'
                        example: 'Y'
                      hometown:
                        type: string
                        example: Newark, NJ
                      jersey_no:
                        type: string
                        example: '12'
                      height:
                        description: Total inches.
                        type: number
                        example: 70
                      weight:
                        description: Pounds.
                        type: number
                        example: 185
                      bio:
                        type: string
                        example: Junior wide receiver.
                      profile_photo_url:
                        type: string
                        maxLength: 2048
                        example: https://example.com/photos/alex.jpg
                    type: object
                type: object
              maxItems: 1000
      responses:
        '200':
          description: Accepted and queued, or already accepted under this session
          content:
            application/json:
              schema:
                properties:
                  data:
                    properties:
                      id:
                        type: integer
                        example: 12
                      status:
                        type: string
                        example: queued
                    type: object
                type: object
        '211':
          description: Validation errors, in CSVBox's server-side validation format
          content:
            application/json:
              schema:
                type: array
                items:
                  properties:
                    type:
                      type: string
                      enum:
                        - cell
                        - table
                      example: cell
                    row_id:
                      description: CSVBox row_number. Cell errors only.
                      type: integer
                      example: 3
                    column:
                      description: Cell errors only.
                      type: string
                      enum:
                        - first_name
                        - last_name
                        - email
                        - phone
                        - sport
                        - school
                        - graduation_year
                        - dob
                        - gender
                        - us_citizen
                      example: email
                    message:
                      type: string
                      example: Email already exists
                  type: object
        '401':
          description: >-
            Missing, invalid, expired or other-environment import token, or the
            agent can no longer import.
          content:
            application/json:
              schema:
                properties:
                  message:
                    type: string
                    example: Unauthenticated.
                type: object
        '422':
          description: >-
            Not a CSVBox delivery (no rows, or a row without row_number or
            row_data).
          content:
            application/json:
              schema:
                properties:
                  message:
                    type: string
                    example: This upload could not be read.
                type: object
      security:
        - csvboxImportToken: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      description: >-
        JWT Bearer token authentication. Use the /api/login endpoint to obtain a
        token.
      scheme: bearer
      bearerFormat: JWT
    csvboxImportToken:
      type: apiKey
      description: >-
        The `import_token` from POST /agent/roster-imports/session. The client
        passes it to the CSVBox importer in `request_headers`, and CSVBox sends
        it on every delivery to the import webhook. Encrypted with this
        environment's app key; it names the agent, the session and the
        environment.
      name: X-Import-Token
      in: header

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.