Owner Abhishek P

V1 API Sync

Source: OpenAPI 3.1.0 spec + Keystone backend source code Tag: Sync - Cursor-based sync endpoints for offline-first clients.

All endpoints require Bearer Token authentication. All use identical cursor-based pagination parameters. Response data is encrypted when feature flag is enabled.

Common Sync Parameters

NameInRequiredConstraintsDescription
course_idqueryrequiredCourseEnumCourse ID
limitqueryoptional1-120, default 10Number of results to return
next_cursorqueryoptionalbase64 stringForward pagination cursor from previous response
prev_cursorqueryoptionalbase64 stringBackward pagination cursor
x-dev-timeheaderoptional13 digitsEpoch ms

How Cursors Work

All sync endpoints use timestamp+id cursors encoded as base64(json({updated_at, id})):

  1. Omit next_cursor for the first request
  2. Backend queries: WHERE updated_at > cursor.updated_at OR (updated_at == cursor.updated_at AND id > cursor.id)
  3. Fetches limit + 1 items. If more than limit, sets has_more=true and returns next_cursor
  4. Pass next_cursor from response into the next request

GET /mcqs/sync

Summary: List PYQ MCQs by Updated At (sync)

Retrieve a paginated list of PYQ MCQs with forward-only pagination by updated_at. Only PYQs are returned (DQ/EQ excluded). Filter by year or taxonomy_id, not both.

Additional Parameters

NameInRequiredDescription
yearqueryoptionalFilter MCQs by year (e.g. 2023). Mutually exclusive with taxonomy_id
taxonomy_idqueryoptionalFilter MCQs by taxonomy ID. Mutually exclusive with year

Condition: year and taxonomy_id are mutually exclusive; error 1003 if both provided.

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8b4567",
      "display_uid": "UPSC-POL-001",
      "short_uid": "M00123",
      "question": "Which article of the Indian Constitution deals with the Right to Equality?",
      "question_format": 1,
      "q_pview": "Which article of the Indian Constitution deals with the Right to Equality?",
      "option_1": "Article 12",
      "selected_option_1_count": 245,
      "option_2": "Article 14",
      "selected_option_2_count": 1893,
      "option_3": "Article 19",
      "selected_option_3_count": 312,
      "option_4": "Article 21",
      "selected_option_4_count": 189,
      "correct_option": "option_2",
      "question_type": 1,
      "year": 2023,
      "root_taxonomy_id": "60d5ec49f1b2c72e4c8b0001",
      "taxonomy_ids": ["60d5ec49f1b2c72e4c8b0001", "60d5ec49f1b2c72e4c8b0002", "60d5ec49f1b2c72e4c8b0003"],
      "sole": "U2FsdGVkX1+abc123...",
      "sort_order": 15,
      "tag_ids": ["60d5ec49f1b2c72e4c8baaaa"],
      "docket_id": "60d5ec49f1b2c72e4c8bdddd",
      "is_deleted": false,
      "ordered_block_ids": ["B001"],
      "block_details": [
        {
          "id": "60d5ec49f1b2c72e4c8beeee",
          "short_uid": "B001",
          "docket_id": "60d5ec49f1b2c72e4c8bdddd",
          "title": "Right to Equality - Key Provisions",
          "conte": "U2FsdGVkX1+xyz789...",
          "is_deleted": false
        }
      ]
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0NDAwMDAwMDAwLCJpZCI6IjYwZDVlYzQ5ZjFiMmM3MmU0YzhiNDU2NyJ9",
    "prev_cursor": null,
    "limit": 10,
    "has_more": true
  }
}

See MCQ docs for full field descriptions.


GET /tags/sync

Summary: Sync Tags by Updated At

Retrieve a paginated list of tags with forward-only pagination based on updated_at.

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8baaaa",
      "name": "Fundamental Rights",
      "updated_at": 1714400000000,
      "created_at": 1714300000000,
      "is_deleted": false
    },
    {
      "id": "60d5ec49f1b2c72e4c8baaab",
      "name": "Directive Principles",
      "updated_at": 1714350000000,
      "created_at": 1714200000000,
      "is_deleted": false
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0MzUwMDAwMDAwLCJpZCI6IjYwZDVlYzQ5ZjFiMmM3MmU0YzhiYWFhYiJ9",
    "prev_cursor": null,
    "limit": 10,
    "has_more": false
  }
}

GET /dockets/sync

Summary: Sync Dockets by Updated At (Lightweight)

Retrieve a lightweight paginated list of dockets for sync. Excludes heavy fields (body, ai_run_results, audio_generated, trending_count, display_uid, tag_ids) to minimize payload size. Includes mcq_ids and mcq_count.

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8bdddd",
      "short_uid": "D00456",
      "title": "Fundamental Rights under Part III",
      "root_taxonomy_id": "60d5ec49f1b2c72e4c8b0001",
      "taxonomy_ids": [
        "60d5ec49f1b2c72e4c8b0001",
        "60d5ec49f1b2c72e4c8b0002",
        "60d5ec49f1b2c72e4c8b0003"
      ],
      "sort_order": 5,
      "is_deleted": false,
      "mcq_ids": ["M00123", "M00124", "M00125"],
      "mcq_count": {
        "pyq_count": 5,
        "dq_count": 2,
        "eq_count": 1
      }
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0NDAwMDAwMDAwfQ==",
    "prev_cursor": null,
    "limit": 10,
    "has_more": true
  }
}
FieldDescription
mcq_idsMCQ short UIDs (not ObjectIds) denormalized from l4_docket_id
mcq_count.pyq_countNumber of PYQ-type MCQs linked to this docket
mcq_count.dq_countNumber of DQ-type MCQs
mcq_count.eq_countNumber of EQ-type MCQs

GET /taxonomy/sync

Summary: Sync Taxonomies by Updated At

Retrieve a paginated list of taxonomies with forward-only pagination based on updated_at. Taxonomies form a hierarchy: L1 (root) -> L2 -> L3 -> L4 (docket leaf).

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8b0001",
      "display_uid": "UPSC-POL",
      "short_uid": "T00001",
      "name": "Polity & Governance",
      "description": "Indian Polity, Constitution, and Governance",
      "parent_id": null,
      "course_id": 1,
      "sort_order": 1,
      "mcq_count": {
        "pyq_count": 450,
        "dq_count": 120,
        "eq_count": 80
      },
      "docket_count": 35,
      "updated_at": 1714400000000,
      "is_deleted": false,
      "status": 2,
      "emoji": "🏛️"
    },
    {
      "id": "60d5ec49f1b2c72e4c8b0002",
      "display_uid": "UPSC-POL-FR",
      "short_uid": "T00002",
      "name": "Fundamental Rights",
      "description": "Rights guaranteed under Part III of the Constitution",
      "parent_id": "60d5ec49f1b2c72e4c8b0001",
      "course_id": 1,
      "sort_order": 1,
      "mcq_count": {
        "pyq_count": 85,
        "dq_count": 20,
        "eq_count": 15
      },
      "docket_count": 8,
      "updated_at": 1714380000000,
      "is_deleted": false,
      "status": 2,
      "emoji": null
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0MzgwMDAwMDAwfQ==",
    "prev_cursor": null,
    "limit": 10,
    "has_more": true
  }
}
FieldDescription
parent_idnull if L1 (root taxonomy). Otherwise, parent taxonomy ObjectId
mcq_countPublished MCQ counts by type for this taxonomy
docket_countNumber of dockets associated with this taxonomy
statusDRAFT=1, PUBLISHED=2, UNPUBLISHED=3, ARCHIVE=4
emojiDisplay emoji, primarily for L1 (root) taxonomies. null for deeper levels
course_idCourseEnum value

GET /years/sync

Summary: Sync Years by Updated At

Retrieve a paginated list of years with published MCQ counts.

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "year": 2023,
      "pyq_count": 150,
      "dq_count": 45,
      "eq_count": 30,
      "is_deleted": false,
      "status": 2
    },
    {
      "year": 2022,
      "pyq_count": 140,
      "dq_count": 50,
      "eq_count": 25,
      "is_deleted": false,
      "status": 2
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0MzAwMDAwMDAwfQ==",
    "prev_cursor": null,
    "limit": 10,
    "has_more": false
  }
}
FieldDescription
pyq_countNumber of published PYQs for this year
dq_countNumber of published DQs
eq_countNumber of published EQs
statusDRAFT=1, PUBLISHED=2, UNPUBLISHED=3, ARCHIVE=4

GET /custom-tests/sync

Summary: Sync Custom Tests by Updated At

Retrieve a paginated list of the user’s custom tests (lightweight, without MCQ details).

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8b9999",
      "short_uid": "CT00456",
      "number_of_mcqs": 20,
      "updated_at": 1714401800000,
      "status": 3,
      "created_at": 1714400000000,
      "is_deleted": false,
      "sort_order": 5,
      "marks": "21.36",
      "l1_tax_ids": ["60d5ec49f1b2c72e4c8b0001", "60d5ec49f1b2c72e4c8b0002"]
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0NDAxODAwMDAwfQ==",
    "prev_cursor": null,
    "limit": 10,
    "has_more": false
  }
}
FieldDescription
statusDISCARDED=1, LIVE=2, SUBMITTED=3
marksDecimal string when submitted; null when LIVE or DISCARDED. Can be negative

See Custom Tests docs for full details.


GET /bookmark-collections/sync

Summary: Sync Bookmark Collections

Get a paginated list of the user’s bookmark collections.

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8bbbbb",
      "name": "All Bookmarks",
      "description": null,
      "is_default": true,
      "course_id": 1,
      "emoji": "📚",
      "mcq_count": 47,
      "short_uid": "BC000",
      "is_deleted": false,
      "created_at": 1714300000000
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": null,
    "prev_cursor": null,
    "limit": 10,
    "has_more": false
  }
}

See Bookmark Collections docs for full details.


GET /mcqs_attrs/sync

Summary: List MCQ Reactions

Retrieve a paginated list of the user’s MCQ attributes (reactions, bookmarks, attempt history).

Success Response

{
  "status": "success",
  "is_data_encrypted": 0,
  "data": [
    {
      "id": "60d5ec49f1b2c72e4c8baaaa",
      "mcq_id": "60d5ec49f1b2c72e4c8b4567",
      "last_attempt_option": "option_2",
      "guessed": false,
      "bookmark_status": 1,
      "bookmark_collection_ids": ["60d5ec49f1b2c72e4c8bcccc"],
      "bookmarked_at": 1714400000000,
      "like_status": 1,
      "root_taxonomy_id": "60d5ec49f1b2c72e4c8b0001",
      "taxonomy_ids": ["60d5ec49f1b2c72e4c8b0001", "60d5ec49f1b2c72e4c8b0002", "60d5ec49f1b2c72e4c8b0003"],
      "year": 2023
    }
  ],
  "error": null,
  "app_actions": null,
  "pagination": {
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoxNzE0NDAwMDAwMDAwfQ==",
    "prev_cursor": null,
    "limit": 10,
    "has_more": true
  }
}

See MCQ Actions docs for full field details.