Custodial

Overview

A Custodial account (UTMA/UGMA) lets an adult irrevocably gift assets to a minor. The Uniform Transfers
to Minors Act and Uniform Gifts to Minors Act are the laws behind it; DriveWealth offers UTMA accounts.

The assets are the minor's property — the minor's SSN is primary for tax reporting — but only the
gifter/custodian can act on the account. The minor can't be given any access to, or information about, the
account. Account assets and control transfer to the minor automatically once they reach age of majority
(AOM) in their state — AOM varies between states.

This is the CUSTODIAL userType. See Creating a user
if you're not sure this is the one you need.

Only stocks are currently supported in Custodial accounts — don't build a flow that assumes other
security types are purchasable once the account is open.

Before you start

You need an existing INDIVIDUAL_TRADER User to act as the custodian/gifter — create that User first (see
Individual trader) and hold onto their id. The minor's User links to it via a
CUSTODIAN_INFO document.

Create the user

The minor is the User being created here — their own identity documents, plus one CUSTODIAN_INFO
document pointing at the custodian's userID.

curl -X POST https://api.drivewealth.com/users \
  -H "Authorization: Bearer $DW_BEARER_TOKEN" \
  -H "dw-client-app-key: $DW_APP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userType": "CUSTODIAL",
    "username": "jane.smith.custodial",
    "password": "my#1Account",
    "documents": [
      {
        "type": "BASIC_INFO",
        "data": {
          "firstName": "Jamie",
          "lastName": "Smith",
          "country": "USA",
          "phone": "12025550149",
          "emailAddress": "[email protected]"
        }
      },
      {
        "type": "IDENTIFICATION_INFO",
        "data": {
          "value": "402724321",
          "type": "SSN",
          "citizenship": "USA"
        }
      },
      {
        "type": "PERSONAL_INFO",
        "data": {
          "birthDay": 3,
          "birthMonth": 12,
          "birthYear": 2015,
          "politicallyExposedNames": "NULL"
        }
      },
      {
        "type": "ADDRESS_INFO",
        "data": {
          "street1": "15 Exchange Place",
          "street2": "Suite 1000",
          "city": "Jersey City",
          "province": "NJ",
          "postalCode": "07302",
          "country": "USA"
        }
      },
      {
        "type": "CUSTODIAN_INFO",
        "data": {
          "userID": "58b843c3-cf2c-4d6e-a27c-29ef7230b830"
        }
      }
    ]
  }'

Replace CUSTODIAN_INFO.data.userID with the id of the existing INDIVIDUAL_TRADER who will act as custodian/gifter — not a placeholder value.

Response — 200

{
  "id": "a8e4150c-ca27-4e7f-bcbb-c8534f09bc56",
  "username": "jane.smith.custodial",
  "userType": {
    "name": "CUSTODIAL",
    "description": "Custodial"
  },
  "status": {
    "name": "PENDING",
    "description": "User is pending approval."
  },
  "parentIB": {
    "id": "80f9b672-120d-4b73-9cc9-42fb3262c4b9",
    "name": "DriveWealth"
  },
  "documents": [
    {
      "type": "ADDRESS_INFO",
      "data": {
        "street1": "15 Exchange Place",
        "street2": "Suite 1000",
        "city": "Jersey City",
        "province": "NJ",
        "postalCode": "07302",
        "country": "USA"
      },
      "description": "Physical address information"
    },
    {
      "type": "BASIC_INFO",
      "data": {
        "firstName": "Jamie",
        "lastName": "Smith",
        "displayName": "JSmith",
        "emailAddress": "[email protected]",
        "phone": "12025550149",
        "country": "USA",
        "language": "en_US"
      },
      "description": "Name, email, phone, etc."
    },
    {
      "type": "CUSTODIAN_INFO",
      "data": {
        "userID": "58b843c3-cf2c-4d6e-a27c-29ef7230b830"
      },
      "description": "Links a custodial user to a parent"
    },
    {
      "type": "IDENTIFICATION_INFO",
      "data": {
        "value": "****4321",
        "type": "SSN",
        "citizenship": "USA",
        "description": "Social Security Number"
      },
      "description": "ID Number and citizenship"
    },
    {
      "type": "PERSONAL_INFO",
      "data": {
        "birthdate": "2015-12-03",
        "politicallyExposedNames": "NULL"
      },
      "description": "Birth date, gender, marital status, etc."
    },
    {
      "type": "TAX_INFO",
      "data": {
        "usTaxpayer": true,
        "taxTreatyWithUS": false
      },
      "description": "Tax Information"
    }
  ],
  "wlpID": "DW",
  "referralCode": "FE1C79",
  "createdWhen": "2026-09-02T19:19:40.068Z",
  "updatedWhen": "2026-09-02T19:19:40.068Z"
}

Document types used here

Same base identity documents as Creating a User, plus:

TypePurposeRequired data
CUSTODIAN_INFOLinks a custodial user to a parentuserID — the custodian's existing INDIVIDUAL_TRADER User id

Complete onboarding

PATCH /users/USERID — attach more documents, correct a field, or sign an attestation.

The body shape is identical to create: a documents array, any subset of types. There's no separate
endpoint for the tax attestation — it's a PATCH carrying a TAX_INFO document with a nested
attestation:

{
  "documents": [
    {
      "type": "TAX_INFO",
      "data": {
        "attestation": {
          "signedBy": "58b843c3-cf2c-4d6e-a27c-29ef7230b830"
        }
      }
    }
  ]
}

And here's a corrected address, the same shape you'd use for any field-level fix:

{
  "documents": [
    {
      "type": "ADDRESS_INFO",
      "data": {
        "street1": "480 Washington Blvd",
        "city": "Jersey City",
        "province": "NJ",
        "postalCode": "07310"
      }
    }
  ]
}

Check KYC status

Two status fields, two different resolutions — know which one you're gating on.

The User object itself (from create, retrieve, or update) only ever reports a coarse status.name:
PENDING or APPROVED. That's enough to gate account creation, but not enough to tell a user why
they're stuck. For that, call the dedicated status endpoint:

curl -X GET https://api.drivewealth.com/users/58b843c3-cf2c-4d6e-a27c-29ef7230b830/kyc-status \
  -H "Authorization: Bearer $DW_BEARER_TOKEN" \
  -H "dw-client-app-key: $DW_APP_KEY"
{
  "userID": "58b843c3-cf2c-4d6e-a27c-29ef7230b830",
  "firstname": "Jamie",
  "lastname": "Smith",
  "identity": {
    "number": "*******-4321",
    "dob": "2015-12-03"
  },
  "accounts": [],
  "kyc": {
    "approved": {
      "timestamp": "2026-08-31T19:11:25.020Z",
      "approvedBy": "80f9b672-120d-4b73-9cc9-42fb3262c4b9"
    },
    "accepted": {
      "acceptedBy": "SYSTEM PRINCIPAL APPROVER",
      "timestamp": "2026-08-31T19:11:25.020Z"
    }
  },
  "status": {
    "name": "APPROVED",
    "description": "User approved."
  },
  "partnerID": {
    "id": "80f9b672-120d-4b73-9cc9-42fb3262c4b9",
    "name": "Uttam_Investment"
  },
  "documents": []
}

kyc.status.name only progresses through these intermediate states for do-KYC partners (DriveWealth
performs screening). For no-KYC/verify-KYC partners, the User is approved as soon as the required documents land.

When it does progress, kyc.status.name is the granular reason code you'll base your next step on:

StatusMeaningNext step
KYC_NOT_READYRequired documents haven't been submitted yetPATCH the missing documents, then wait for the update event
KYC_READYDocuments complete, queued for processingWait for the update event, no action needed
KYC_PROCESSINGVerification in progressWait for the update event, no action needed
KYC_INFO_REQUIREDPII didn't match — needs a corrected PATCHSend the user back into your onboarding UI to fix the mismatched field(s), then PATCH
KYC_DOC_REQUIREDA supporting document is missingPrompt for the specific missing document, then PATCH
KYC_MANUAL_REVIEWEscalated to a human reviewerShow an "under review" state and wait — this can take longer than the automated states
KYC_APPROVEDTerminal — clearedProceed to open an Account
KYC_DENIEDTerminal — rejectedSurface the denial, don't retry the same payload

For do-KYC partners, don't poll this endpoint as your primary signal — subscribe to the kyc.created / kyc.updated event on your SQS queue. Its payload.current.status already carries the new kyc.status.name value directly, so you don't need to re-fetch kyc-status just to learn what changed:

{
  "id": "event_789ca9ba-312b-4480-9f36-3805ceb00f63",
  "type": "kyc.updated",
  "ibID": "80f9b672-120d-4b73-9cc9-42fb3262c4b9",
  "object": "KYC_UPDATED",
  "timestamp": "2026-07-23T07:08:14.121518146Z",
  "payload": {
    "current": {
      "status": "KYC_APPROVED",
      "statusMessage": "KYC Approved",
      "details": ["POOR_PHOTO_QUALITY"]
    },
    "previous": {
      "status": "KYC_APPROVED",
      "statusMessage": "KYC Approved",
      "details": ["POOR_PHOTO_QUALITY"]
    },
    "userID": "58b843c3-cf2c-4d6e-a27c-29ef7230b830"
  }
}

Compare payload.current.status against payload.previous.status to confirm it actually changed, and use payload.userID to look up the User in your own system. Reserve polling kyc-status for an occasional reconciliation pass (catching a missed or delayed event), not the main driver of your UI.

Requirements vary by country

UTMA/UGMA is a US legal structure. Everything above assumes a USA minor and a USA custodian — don't assume it generalizes:

  • Non-US minors/custodians: the UTMA/UGMA framework is US-specific. A different country may have no equivalent structure, or a materially different one (different transfer-of-control rules, different
    irrevocability rules, different tax treatment). Confirm with DriveWealth and the relevant regulatory
    bodies before assuming this flow applies outside the US.
  • ID type: IDENTIFICATION_INFO.type accepts more than SSNPASSPORT, ALIEN_ID, OTHER, etc., and non-SSN types require issuingCountry.
  • "Doc countries": some countries require an actual scanned/photographed document (ID, proof of
    address) to clear KYC, not just the structured data fields above — that upload goes through the
    separate Physical Documents API, not documents[] on this endpoint.

What's next

Once the minor's User clears KYC, open their Account and reference the custodian relationship there (see
the Accounts API).


Did this page help you?