APIs & services / portfolio-service

Create a crop plan entry (portfolio with one line item).

Endpointtoday's app
Call
POST /gateway/portfolio-service/api/v1/portfolios/line-items/v2?logCropActivity=true&actorType&actorId
Service
portfolio-service
Features
Crop Actual (portfolio), Crop Plan, PMDS and A Grade crop details
When
Queued sync (IE_PORTFOLIO POST)
Auth
X-SESSIONID stamped when queued (CoreAPI dooWithRetry); X-REQUESTID idempotency key
In the App Service

Use case

Queued create of a crop plan/actual entry (IE_PORTFOLIO POST via IEPortfolioWorker) after the user saves a crop on a land. The portfolio carries exactly one line item with its crops and PMDS answers. IEPortfolioPostRH maps returned PLI/PPLI ids back by uuid, sets the portfolio id and clears a previous sync error. On ERR_PLATFORM_ASSET_CHECKSUM_MISMATCH or ..._TOTAL_QUANTITY_LIMIT_EXCEED it schedules FetchFarmerAssetDataWorker to refresh the land; it also refreshes owningEntityId/assetId/assetCheckSum in the queued body and retries later.

Headers

HeaderValueNotes
X-SESSIONID<session token>Stamped when the operation is queued (dooWithRetry) and re-set by CoreAPI at send time; refreshed on 401.
X-REQUESTID<uuid v4>Idempotency key generated by OperationManagerImpl.createOperation for POST/PUT/PATCH when the op enters the offline queue.
X-ACK-REQUESTID<X-REQUESTID of an earlier completed op>Added by SyncQueueProcessor for POST/PUT/PATCH: acknowledges a request id the device already saw succeed.
Content-Typeapplication/jsonAdded by CoreAPI.

Parameters

ParamInRequiredExampleNotes
logCropActivityqueryYestrueAlways true.
actorTypequeryYesIEBRIDGE_USER when appMillMetadata.ie_appId is numeric, else IE.
actorIdqueryNo40001Farmer IE id when known.

Request

Example request body (synthetic data)
{
  "owningEntityId": "40001",
  "id": 5001,
  "uuid": "3a9d0000-0000-4000-8000-000000000101",
  "ownerEntityType": {
    "code": "ENTITY_TYPE.IE",
    "refClass": {
      "code": "CLASS.ENTITY_TYPE"
    }
  },
  "appMillMetadata": {
    "ie_appId": "ie-alias-0001"
  },
  "actor": {
    "actorId": "40001",
    "id": 0,
    "actorType": "ACTOR_TYPE.IE"
  },
  "portfolioLineItemList": [
    {
      "portfolioId": "3a9d0000-0000-4000-8000-000000000101",
      "id": 6001,
      "asset": "ASSET-CODE-0001",
      "uuid": "5f6c1d2e-0000-4000-8000-00000000a001",
      "practiceMasterCodes": [
        "PRACTICE.MULCHING",
        "PRACTICE.SEED_TREATMENT"
      ],
      "portfolioLineItemType": "PORTFOLIO_LINE_ITEM_TYPE.ACTUAL",
      "imageUrl": "/data/user/0/<app>/files/pli_6001.jpg",
      "isActive": true,
      "currentSeason": "KHARIF_2026",
      "methodMaster": {
        "id": 3,
        "uuid": "m-0003",
        "methodCode": "METHOD.PMDS"
      },
      "practiceMaster": {
        "practiceCode": "PRACTICE.MULCHING",
        "id": 4,
        "uuid": "p-0004"
      },
      "appCreatedAt": "1780000000000",
      "appLastModifiedAt": "1780000000000",
      "createdAt": null,
      "lastModifiedAt": null,
      "titleId": "TILE-0001",
      "assetId": 9001,
      "assetCheckSum": "<asset checksum>",
      "hasSyncError": false,
      "syncErrorId": null,
      "compactBlockId": null,
      "isLinkedToCompactBlock": false,
      "createdByApp": 109,
      "lastModifiedByApp": 109,
      "createdFromDevice": "<device id>",
      "lastUpdatedFromDevice": "<device id>",
      "geoFencingFileName": null,
      "geoFencingCreatedOn": null,
      "geoFencingFilePath": null,
      "geoFenceAttachments": null,
      "isEditable": true,
      "imageAttachmentId": null,
      "imageCreatedAt": null,
      "changeLog": null,
      "cropSeason": "KHARIF",
      "isAttachmentFetched": false,
      "portfolioProductLineItemList": [
        {
          "portfolioLineItemId": "5f6c1d2e-0000-4000-8000-00000000a001",
          "id": 7001,
          "uuid": "7b1e0000-0000-4000-8000-000000000201",
          "genericProduct": "PRODUCT.PADDY",
          "plannedStartDate": "2026-06-01",
          "plannedEndDate": "2026-10-31",
          "actualStartDate": "2026-06-05",
          "actualEndDate": null,
          "cropChangeReason": null,
          "assetProportion": {
            "id": 0,
            "uoM": {
              "code": "UOM.ACRE",
              "description": "Acre",
              "measurementType": {
                "code": "MEASUREMENT_TYPE.AREA",
                "description": "Area",
                "globalRefClass": {
                  "code": "GREF.MEASUREMENT_TYPE",
                  "description": "",
                  "name": "Measurement type"
                },
                "name": "Area"
              },
              "name": "Acre",
              "symbol": "ac"
            },
            "value": 0.5
          },
          "isMajorCrop": true,
          "cropType": "CROP_TYPE.MAIN",
          "harvestStatus": "HARVEST_STATUS.PENDING",
          "harvestStartDateTime": null,
          "harvestCompleteDateTime": null,
          "appCreatedAt": "1780000000000",
          "appLastModifiedAt": "1780000000000",
          "createdAt": null,
          "lastModifiedAt": null,
          "ppliCropType": "PPLI_CROP_TYPE.PMDS",
          "expectedHarvestDate": "2026-10-15",
          "seedSource": "SEED_SOURCE.OWN",
          "seedVariety": "Variety A",
          "interestedInCropMarketing": false,
          "sowingDate": "2026-06-05",
          "appId": "7b1e0000-0000-4000-8000-000000000201",
          "syncStatus": "SYNC_PENDING"
        }
      ],
      "portfolioLineItemQuestionResponses": [
        {
          "portfolioLineItemAppId": "5f6c1d2e-0000-4000-8000-00000000a001",
          "id": 0,
          "uuid": "8c2f0000-0000-4000-8000-000000000301",
          "questionId": 12,
          "category": "PMDS",
          "subCategory": "SEED_TREATMENT",
          "questionType": "SINGLE_SELECT",
          "questionCode": "Q_SEED_TREATMENT",
          "questionLabel": "Seed treated?",
          "answerLanguageCode": "en",
          "answerValue": "YES",
          "answerLabel": "Yes",
          "answerText": null,
          "appCreatedAt": "1780000000000",
          "appLastModifiedAt": "1780000000000"
        }
      ],
      "isSelected": false,
      "isPresentInDifferentCB": false,
      "appId": "5f6c1d2e-0000-4000-8000-00000000a001",
      "syncStatus": "SYNC_PENDING"
    }
  ],
  "appId": "3a9d0000-0000-4000-8000-000000000101",
  "syncStatus": "SYNC_PENDING"
}
FieldTypeRequiredNotes
owningEntityIdstringYesFarmer IE id (filled from alias on retry).
uuid / idstring / integerYesPortfolio client uuid; id 0/null on first create.
ownerEntityType.codestringYesENTITY_TYPE.IE.
actorobjectNoactorId = owningEntityId, actorType ACTOR_TYPE.IE.
appMillMetadata.ie_appIdstringNoFarmer alias (local link).
portfolioLineItemList[].idintegerNoServer PLI id (0/null on first create). Column portfolio_portfoliolineitem.id.
portfolioLineItemList[].uuidstringYesClient UUID; server echoes it so the RH can map ids back.
portfolioLineItemList[].asset / assetId / assetCheckSumstring / integer / stringYesLand (asset) code, server asset id and checksum from the Land Assets table. Checksum mismatch -> ERR_PLATFORM_ASSET_CHECKSUM_MISMATCH.
portfolioLineItemList[].titleIdstringNoLand tile id used to match geofence files and compact blocks.
portfolioLineItemList[].portfolioLineItemTypestringYesPLAN vs ACTUAL (crop plan / crop actual screen).
portfolioLineItemList[].currentSeason / cropSeasonstringNoFrom app config CURRENT_SEASON.
portfolioLineItemList[].practiceMasterCodesstring[]NoPractices ticked on the crop actual screen (practicemaster table).
portfolioLineItemList[].methodMaster / practiceMasterobjectNoMethod (PMDS/RDS) and practice selected.
portfolioLineItemList[].portfolioProductLineItemList[]arrayYesOne entry per crop on the land: genericProduct, cropType, ppliCropType, assetProportion (area value + UoM), planned/actual dates, sowingDate, seedSource, seedVariety, expectedHarvestDate, harvestStatus, isMajorCrop.
portfolioLineItemList[].portfolioLineItemQuestionResponses[]arrayNoPMDS A-grade question answers (portfolio_question_response table).
portfolioLineItemList[].createdByApp / lastModifiedByApp / createdFromDevice / lastUpdatedFromDevicelong / stringNoAudit fields set by the app.
portfolioLineItemList[].appCreatedAt / appLastModifiedAtstring (epoch ms)NoDevice timestamps.
portfolioLineItemList[].imageUrl, geoFencing*, hasSyncError, syncErrorId, isEditable, isSelected, isPresentInDifferentCB, practices, appId, opId, syncStatusmixedNoLocal-only columns that Gson also serialises; the server ignores them.

Response

Example response body (synthetic data)
{
  "owningEntityId": "40001",
  "id": 5001,
  "uuid": "3a9d0000-0000-4000-8000-000000000101",
  "ownerEntityType": {
    "code": "ENTITY_TYPE.IE",
    "refClass": {
      "code": "CLASS.ENTITY_TYPE"
    }
  },
  "actor": {
    "actorId": "40001",
    "id": 0,
    "actorType": "ACTOR_TYPE.IE"
  },
  "portfolioLineItemList": [
    {
      "portfolioId": "3a9d0000-0000-4000-8000-000000000101",
      "id": 6001,
      "asset": "ASSET-CODE-0001",
      "uuid": "5f6c1d2e-0000-4000-8000-00000000a001",
      "practiceMasterCodes": [
        "PRACTICE.MULCHING",
        "PRACTICE.SEED_TREATMENT"
      ],
      "portfolioLineItemType": "PORTFOLIO_LINE_ITEM_TYPE.ACTUAL",
      "imageUrl": "/data/user/0/<app>/files/pli_6001.jpg",
      "isActive": true,
      "currentSeason": "KHARIF_2026",
      "methodMaster": {
        "id": 3,
        "uuid": "m-0003",
        "methodCode": "METHOD.PMDS"
      },
      "practiceMaster": {
        "practiceCode": "PRACTICE.MULCHING",
        "id": 4,
        "uuid": "p-0004"
      },
      "appCreatedAt": "1780000000000",
      "appLastModifiedAt": "1780000000000",
      "createdAt": null,
      "lastModifiedAt": null,
      "titleId": "TILE-0001",
      "assetId": 9001,
      "assetCheckSum": "<asset checksum>",
      "hasSyncError": false,
      "syncErrorId": null,
      "compactBlockId": null,
      "isLinkedToCompactBlock": false,
      "createdByApp": 109,
      "lastModifiedByApp": 109,
      "createdFromDevice": "<device id>",
      "lastUpdatedFromDevice": "<device id>",
      "geoFencingFileName": null,
      "geoFencingCreatedOn": null,
      "geoFencingFilePath": null,
      "geoFenceAttachments": null,
      "isEditable": true,
      "imageAttachmentId": null,
      "imageCreatedAt": null,
      "changeLog": null,
      "cropSeason": "KHARIF",
      "isAttachmentFetched": false,
      "portfolioProductLineItemList": [
        {
          "portfolioLineItemId": "5f6c1d2e-0000-4000-8000-00000000a001",
          "id": 7001,
          "uuid": "7b1e0000-0000-4000-8000-000000000201",
          "genericProduct": "PRODUCT.PADDY",
          "plannedStartDate": "2026-06-01",
          "plannedEndDate": "2026-10-31",
          "actualStartDate": "2026-06-05",
          "actualEndDate": null,
          "cropChangeReason": null,
          "assetProportion": {
            "id": 0,
            "uoM": {
              "code": "UOM.ACRE",
              "description": "Acre",
              "measurementType": {
                "code": "MEASUREMENT_TYPE.AREA",
                "description": "Area",
                "globalRefClass": {
                  "code": "GREF.MEASUREMENT_TYPE",
                  "description": "",
                  "name": "Measurement type"
                },
                "name": "Area"
              },
              "name": "Acre",
              "symbol": "ac"
            },
            "value": 0.5
          },
          "isMajorCrop": true,
          "cropType": "CROP_TYPE.MAIN",
          "harvestStatus": "HARVEST_STATUS.PENDING",
          "harvestStartDateTime": null,
          "harvestCompleteDateTime": null,
          "appCreatedAt": "1780000000000",
          "appLastModifiedAt": "1780000000000",
          "createdAt": null,
          "lastModifiedAt": null,
          "ppliCropType": "PPLI_CROP_TYPE.PMDS",
          "expectedHarvestDate": "2026-10-15",
          "seedSource": "SEED_SOURCE.OWN",
          "seedVariety": "Variety A",
          "interestedInCropMarketing": false,
          "sowingDate": "2026-06-05",
          "appId": "7b1e0000-0000-4000-8000-000000000201",
          "syncStatus": "SYNC_PENDING"
        }
      ],
      "portfolioLineItemQuestionResponses": [
        {
          "portfolioLineItemAppId": "5f6c1d2e-0000-4000-8000-00000000a001",
          "id": 0,
          "uuid": "8c2f0000-0000-4000-8000-000000000301",
          "questionId": 12,
          "category": "PMDS",
          "subCategory": "SEED_TREATMENT",
          "questionType": "SINGLE_SELECT",
          "questionCode": "Q_SEED_TREATMENT",
          "questionLabel": "Seed treated?",
          "answerLanguageCode": "en",
          "answerValue": "YES",
          "answerLabel": "Yes",
          "answerText": null,
          "appCreatedAt": "1780000000000",
          "appLastModifiedAt": "1780000000000"
        }
      ],
      "isSelected": false,
      "isPresentInDifferentCB": false,
      "appId": "5f6c1d2e-0000-4000-8000-00000000a001",
      "syncStatus": "SYNC_PENDING"
    }
  ]
}
FieldTypeRequiredNotes
idintegerportfolio.id.
owningEntityIdstringSaved with the portfolio.
portfolioLineItemList[].id / uuid and portfolioProductLineItemList[].id / uuidmixedupdateOptionLineItemIds maps server ids onto local rows by uuid.

Errors

StatusWhenWhat the app does
4xx with errorCode ERR_PLATFORM_ASSET_CHECKSUM_MISMATCH / ERR_PLATFORM_ASSET_TOTAL_QUANTITY_LIMIT_EXCEEDLand changed on server or area over limitFetchFarmerAssetDataWorker refreshes the land asset; body updated with latest assetId/checksum; retried later; shown in Sync Error Log.
401Session token expired or invalidCoreAPI invalidates the session id and retries once immediately; if still failing the op stays in the queue (TryAgain.LATER).
500Server error on a /gateway URLBaseResponseHandler fetches a new session id (first 5 attempts) and returns TryAgain.LATER; retried per RetryScheduler policy (retry threshold/delay come from app config).
503Service unavailableCoreAPI retries once immediately, then the op is retried later by the sync queue.
any non-2xxAny other failureHandler onError -> TryAgain.LATER (op stays queued and is visible in Pending Sync); a row is written to the local error_log table (Sync Error Log screen) with URL, body, headers and raw response; every 10th attempt the op is mirrored to Firestore for support.

Source

  • data/src/main/java/org/mindtree/app/agricultor/data/entity/Portfolio.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/entity/PortfolioLineItem.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/work/IEPortfolioWorker.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/IEPortfolioPostRH.kt
  • app/src/main/java/org/platformcommons/urvi/bridge/BridgeSyncHelper.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/manager/CoreAPIManager.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/BaseResponseHandler.kt
  • offline-core/src/main/java/com/mindtree/bridge/coreapi/offline/SyncQueueProcessor.java
  • offline-core/src/main/java/com/mindtree/bridge/coreapi/offline/manager/OperationManagerImpl.java
  • core-api/src/main/java/com/mindtree/bridge/coreapi/impl/CoreAPIDefaultImpl.java