---
# System prepended metadata

title: API Field Documentation

---

# API Field Documentation

## Overview

This document describes the fields in the API response, including their definitions and logic used for computation.

### How to generate an API token
```ruby
my_key = ApiKey.create!(consumer_name: "Example key")
token = my_key.key_string
```

### Defined route and controller files
```ruby!
    namespace :v2 do
      resources :appeals, only: :index
    end
```

The most relevant controller file is: `app/controllers/api/v2/appeals_controller.rb`
and the data is primarily generated via this file: `app/services/api/v2/appeals.rb`

### Serializers
There are 4 relevant serializers for this API.
Higher-Level Reviews: `app/models/serializers/v2/hlr_status_serializer.rb`
Supplemental Claims: `app/models/serializers/v2/sc_status_serializer.rb`
Appeals: `app/models/serializers/v2/appeal_status_serializer.rb`
Legacy Appeals: `app/models/serializers/v2/legacy_appeal_status_serializer.rb`

### Example curl request to the api
```
curl -X GET -H "Authorization: Token dba6a974c0de4e33996a33c0eac886d0" -H "Content-Type: application/json" -H "X-VA-Receipt-Date: 2025-02-05" -H "ssn: 252866460" "http://localhost:3000/api/v2/appeals"
```

### Example api return
```json
{
  "data": [
    {
      "id": "HLR80",
      "type": "higherLevelReview",
      "attributes": {
        "appealIds": ["HLR80"],
        "updated": "2025-02-06T17:01:16-05:00",
        "incompleteHistory": false,
        "active": true,
        "description": "Streptococcic arthritis",
        "location": "aoj",
        "aoj": "vha",
        "programArea": "medical",
        "status": {
          "type": "hlr_received",
          "details": {}
        },
        "alerts": [],
        "issues": [
          {
            "active": true,
            "lastAction": null,
            "date": null,
            "description": "Streptococcic arthritis",
            "diagnosticCode": "5008"
          }
        ],
        "events": [
          {
            "type": "hlr_request",
            "date": "2024-12-30"
          }
        ],
        "evidence": []
      }
    }
  ]
}
```

## Fields

### `appeal_ids`
#### Attribute Description
Resolved by `linked_review_ids`, which is an array containing a single string.

#### Possible values
`string` since it's an infinite number of integers


#### Code snippets
```ruby
def review_status_id
  "HLR#{id}"
end

def linked_review_ids
  Array.wrap(review_status_id)
end
```

### `updated`

#### Attribute Description
Always the current time, converted to Eastern Time and rounded into an ISO 8601 date string.

#### Possible values
`ISO 8601 date string`

#### Code snippets
```ruby
Time.zone.now.in_time_zone("Eastern Time (US & Canada)").round.iso8601
```

### `incomplete_history`
#### Attribute Description
Always `false` for non-legacy appeals. For legacy appeals it is marked as true if any of the nodes of the generated appeal series is marked as incomplete which is a node without a parent in the tree

#### Possible values
`boolean` : true or false

#### Code snippets
```ruby
    # for other review types
    attribute :incomplete_history do
      false
    end

    # For legacy appeals
    appeal_tree_nodes.each do |node|
      # Set the series, joining through the merge table to the series table.
      appeal_series = series_table[merge_table[node[:series_id]]]
      appeal_series.appeals << node[:appeal]
      # If any node is marked as incomplete, the series is marked as incomplete.
      appeal_series.update(incomplete: true) if node[:incomplete]
    end
```

### `active`
#### Attribute Description
A boolean indicating active status, determined by the `active_status?` method. Factors include benefit type, existing EPS, and active tasks associated with the review.

#### Possible values
`boolean` : true or false

#### Code snippets
```ruby
  # For appeals
  def active_status?
    # For the appeal status api, and Appeal is considered open
    # as long as there are active remand claim or effectuation
    # tracked in VBMS.
    active? || active_effectuation_ep? || active_remanded_claims?
  end

  # for HLRs active? includes End product check as well
  def active_status?
    # for the purposes for appeal status api, an HLR is considered active if there are
    # still active remand claims.
    active? || active_remanded_claims?
  end

  # SCs same as HLRs but without the active remand check
  def active_status?
    active?
  end

  # Legacy appeal
  def active?
    # All issues on an appeal have not yet been granted or denied
    status != "Complete"
  end
```

### `description`
#### Attribute Description
A string representing the number of request issues associated with a review. It can be derived from diagnostic codes, claim types, or issue count. Implementation:

#### Possible values
`string` : it can be a very large number of possibilities since it's issue count + benefit types and other things

#### Code snippets
```ruby
  def description
    return if request_issues.empty?

    descripton = fetch_status_description_using_diagnostic_code
    return descripton if descripton

    description = fetch_status_description_using_claim_type
    return description if description

    return "1 issue" if request_issues.count == 1

    "#{request_issues.count} issues"
  end
  
  def fetch_status_description_using_diagnostic_code
    issue = request_issues.find do |ri|
      !ri[:contested_rating_issue_diagnostic_code].nil?
    end

    description = issue.api_status_description if issue
    return unless description

    return description if request_issues.count - 1 == 0

    return "#{description} and 1 other" if request_issues.count - 1 == 1

    "#{description} and #{request_issues.count - 1} others"
  end
  
  def fetch_status_description_using_claim_type
    return if program == "other" || program == "multiple"

    return "1 #{program} issue" if request_issues.count == 1

    "#{request_issues.count} #{program} issues"
  end
```

### `aod`
#### Attribute description
An attribute depicting whether or not an appeal or legacy appeal has been marked as advanced on docket. It is resolved by the `advanced_on_docket?` method for appeals and the `aod` attribute on legacy appeals

**Special note: This attribute is only present on Appeals and Legacy Appeals not HLRs and SCs**

#### Possible values
`boolean`: True or false based on the aod status

#### Code snippets
```ruby
  # For Appeals. It is based on several different criteria often related to the claimant model  
  def advanced_on_docket?
    conditionally_set_aod_based_on_age
    # One of the AOD motion reasons is 'age'. Keep interrogation of any motions separate from `aod_based_on_age`,
    # which reflects `claimant.advanced_on_docket_based_on_age?`.
    aod_based_on_age || claimant&.advanced_on_docket_motion_granted?(self)
  end

  #For legacy appeals it comes straight out of Vacols
  def aod(vacols_id)
    VACOLS::Case.aod([vacols_id])[vacols_id]
  end
```

### `location`
#### Attribute description
Always `"aoj"` for SCs and HLRs. For appeals and legacy appeals, determined by active EPs, remands, or latest appeal status. Implementation:

#### Possible values
`enum`: the value can only be "aoj" or "bva" regardless of review type

#### Code snippets
```ruby
  # Hlrs and SCs
  attribute :location do
    "aoj"
  end

  # Appeals
  def location
    if active_effectuation_ep? || active_remanded_claims?
      "aoj"
    else
      "bva"
    end
  end

# Legacy appeals
  def location
    %w[Advance Remand].include?(latest_appeal.status) ? :aoj : :bva
  end
```

### `aoj`
#### Attribute description
Derived from request issues and benefit type. For appeals, it may be "other" if issues have different benefit types. For legacy appeals, it finds the first non-nil `aoj` value from all issues or defaults to "other".

#### Possible values
`string` or possibly `enumeration`: The total number of values seems to be fixed based on all the possible benefit types, the list of aoj values from legacy appeals, and "other". If the legacy aoj values are non variable then it can be considered an enumeration


#### Code snippets
```ruby
  # For HLRs and SCs
  def aoj
    return if request_issues.empty?

    request_issues.first.api_aoj_from_benefit_type
  end
 
  # For appeals
  def aoj
    return if request_issues.empty?

    return "other" unless all_request_issues_same_aoj?

    request_issues.first.api_aoj_from_benefit_type
  end

  # Legacy appeal version
  def aoj
    appeals.lazy.flat_map(&:issues).map(&:aoj).find { |aoj| !aoj.nil? } || :other
  end

  # The request issue method that decides the aoj type
  def api_aoj_from_benefit_type
    case benefit_type
    when "compensation", "pension", "fiduciary", "insurance", "education", "voc_rehab", "loan_guaranty"
      "vba"
    else
      benefit_type
    end
  end
```

### `program`
#### Attribute description
Determines the benefit type for HLRs and SCs. For appeals, if all request issues share the same benefit type, that type is used; otherwise, it is "multiple".

**Special note: For Appeals, HLRs, and SCs the benefit types listed below get changed to "vba"
"compensation", "pension", "fiduciary", "insurance", "education", "voc_rehab", and "loan_guaranty"**

#### Possible values
`enumeration`: It can only be one of the following values
vre, medical, burial, multiple, compensation, pension, fiduciary, insurance, education, voc_rehab, loan_guaranty, nca, vha, vba_burial, compensation, education, insurance, loan_guaranty, medical, pension, vre, other, bva, nca_burial, vba, or fiduciary

#### Code snippets

```ruby
  # HLRs and SCs
  def program
    case benefit_type
    when "voc_rehab"
      "vre"
    when "vha"
      "medical"
    when "nca"
      "burial"
    else
      benefit_type
    end
  end

  #Appeal version
  def program
    return if request_issues.empty?

    if request_issues.all? { |ri| ri.benefit_type == request_issues.first.benefit_type }
      request_issues.first.benefit_type
    else
      "multiple"
    end
  end

  # Benefit types
  {
    "compensation": "Compensation",
    "pension": "Pension & Survivor's Benefits",
    "fiduciary": "Fiduciary",
    "insurance": "Insurance",
    "education": "Education",
    "voc_rehab": "Veterans Readiness and Employment",
    "loan_guaranty": "Loan Guaranty",
    "nca": "National Cemetery Administration",
    "vha": "Veterans Health Administration"
  }

  # For legacy appeals it is either one of these values from the constant hash below or "multiple"
  PROGRAMS = {
    "01" => :vba_burial,
    "02" => :compensation,
    "03" => :education,
    "04" => :insurance,
    "05" => :loan_guaranty,
    "06" => :medical,
    "07" => :pension,
    "08" => :vre,
    "09" => :other,
    "10" => :bva,
    "11" => :nca_burial,
    "12" => :fiduciary
  }.freeze
```

### `status`
#### Attribute description
Fetched via a serializer with attributes `type` and `details`.
```ruby
  attribute :type, &:fetch_status
  attribute :details, &:fetch_details_for_status
```

##### type attribute
##### Sub Attribute description
A sub attribute of the status attribute that is aliased to the `fetch_status` method

##### Possible values
`enumeration`: The list of values is a fixed number of strings but it is scattered around in 10+ methods: TODO: Build a complete list of values for appeals and legacy appeals
Possible values for `fetch_status`:
- HLR: `:hlr_received`, `:hlr_dta_error`, `:hlr_decision`, and `:hlr_closed`
- SC: `:sc_received`, `:sc_closed`, and `:sc_decision`
- Appeals: Multiple status types including `:pre_docketed`, `:decision_in_progress`, `:bva_decision`, etc.
- Legacy appeals: Same as appeals it includes a large amount of status types

Appeal values
```ruby
:pre_docketed,
:pending_hearing_scheduling,
:scheduled_hearing,
:evidentiary_period,
:at_vso,
:decision_in_progress,
:on_docket,
:ama_remand, 
:post_bva_dta_decision,
:bva_decision_effectuation,
:bva_decision,
:decision_in_progress,
:withdrawn,
:other_close
```
Legacy Appeal values:
```ruby
[
  :scheduled_hearing,
  :pending_hearing_scheduling,
  :on_docket,
  :pending_certification_ssoc,
  :pending_certification,
  :pending_form9,
  :pending_soc,
  :stayed,
  :at_vso,
  :bva_development,
  :decision_in_progress,
  :bva_decision,
  :field_grant,
  :withdrawn,
  :ftr,
  :ramp,
  :statutory_opt_in,
  :death,
  :reconsideration,
  :merged,
  :other_close,
  :remand_ssoc,
  :remand,
  :motion,
  :cavc
]
```

##### Code snippets

```ruby
  # For HLRs
  def fetch_status
    if active?
      :hlr_received
    elsif active_remanded_claims?
      :hlr_dta_error
    elsif remand_supplemental_claims.any?
      remand_supplemental_claims.each do |rsc|
        return :hlr_decision if rsc.decision_issues.any?
      end
      :hlr_closed
    else
      decision_issues.empty? ? :hlr_closed : :hlr_decision
    end
  end

  # For SCs
  def fetch_status
    if active?
      :sc_recieved
    else
      decision_issues.empty? ? :sc_closed : :sc_decision
    end
  end

  # For appeals and more unlisted chained method calls
  def fetch_status
    if open_pre_docket_task?
      :pre_docketed
    elsif active?
      fetch_pre_decision_status
    else
      fetch_post_decision_status
    end
  end

  # for legacy appeals and more unlisted chained method calls
  def fetch_status
    case latest_appeal.status
    when "Advance"
      disambiguate_status_advance
    when "Active"
      disambiguate_status_active
    when "Complete"
      disambiguate_status_complete
    when "Remand"
      disambiguate_status_remand
    when "Motion"
      :motion
    when "CAVC"
      :cavc
    end
  end
```

##### description attribute
##### Sub Attribute description
A sub attribute of the status attribute that is aliased to the `fetch_details_for_status` method

##### Possible values
`hash` or `object` - It builds json for all the fetched decision issues on the review based on benefit type and disposition
The json will often end up looking like this. There are other keys for hearings and other types of statuses sometimes 
```ruby
      {
        issues: api_issues_for_status_details_issues(issue_list)
      }
      # The issues values
      {
        description: issue.api_status_description,
        disposition: issue.api_status_disposition
      }
```


##### Code snippets
```ruby
  # HLRs
  def fetch_details_for_status
    case fetch_status
    when :hlr_decision
      issue_list = fetch_all_decision_issues
      {
        issues: api_issues_for_status_details_issues(issue_list)
      }
    else
      {}
    end
  end

  # SCs
  def fetch_details_for_status
    case fetch_status
    when :sc_decision
      {
        issues: api_issues_for_status_details_issues
      }
    else
      {}
    end
  end

  # Appeals
  def fetch_details_for_status # rubocop:disable Metrics/MethodLength
    case fetch_status
    when :bva_decision
      {
        issues: api_issues_for_status_details_issues(decision_issues)
      }
    when :ama_remand
      {
        issues: api_issues_for_status_details_issues(decision_issues)
      }
    when :post_bva_dta_decision
      post_bva_dta_decision_status_details
    when :bva_decision_effectuation
      {
        bva_decision_date: decision_event_date,
        aoj_decision_date: decision_effectuation_event_date
      }
    when :pending_hearing_scheduling
      {
        type: "video"
      }
    when :scheduled_hearing
      api_scheduled_hearing_status_details
    when :decision_in_progress
      {
        decision_timeliness: AppealSeries::DECISION_TIMELINESS.dup
      }
    else
      {}
    end
  end

  # Legacy Appeals
  def fetch_details_for_status
    case status
    when :scheduled_hearing
      hearing = latest_appeal.scheduled_hearings.min_by(&:scheduled_for)

      {
        date: hearing.scheduled_for.to_date,
        type: hearing.readable_request_type.downcase,
        location: hearing.request_type_location
      }
    when :pending_hearing_scheduling
      { type: latest_appeal.current_hearing_request_type }
    when :pending_form9, :pending_certification, :pending_certification_ssoc
      {
        last_soc_date: last_soc_date,
        certification_timeliness: CERTIFICATION_TIMELINESS.dup,
        ssoc_timeliness: SSOC_TIMELINESS.dup
      }
    when :pending_soc
      { soc_timeliness: SOC_TIMELINESS.dup }
    when :at_vso
      { vso_name: representative_name }
    when :decision_in_progress
      { decisionTimeliness: DECISION_TIMELINESS.dup }
    when :remand
      {
        issues: issues_for_last_decision,
        remand_timeliness: REMAND_TIMELINESS.dup
      }
    when :remand_ssoc
      {
        last_soc_date: last_soc_date,
        return_timeliness: RETURN_TIMELINESS.dup,
        remand_ssoc_timeliness: REMAND_SSOC_TIMELINESS.dup
      }
    when :bva_decision
      { issues: issues_for_last_decision }
    else
      {}
    end
  end


  # Issues get build by this. Which ends up being different based on benefit type and disposition
  def api_issues_for_status_details_issues(issue_list)
    issue_list.map do |issue|
      {
        description: issue.api_status_description,
        disposition: issue.api_status_disposition
      }
    end
```

### `alerts`

#### Attribute description
An array of objects that varies based on the type of decision review and relevant data that is sorted by decision date

#### Possible values
`array`: It's an array of objects of this type of structure. The keys change based on the type of alert that is built

```ruby
    {
      type: "ama_post_decision",
      details:
      {
        decisionDate: decision_review.decision_effectuation_event_date,
        availableOptions: decision_review.available_review_options,
        dueDate: decision_review.decision_effectuation_event_date + 365.days,
        cavcDueDate: decision_review.decision_effectuation_event_date + 120.days
      }
    }
```


#### Code snippets
```ruby
  def alerts
    @alerts ||= ApiStatusAlerts.new(decision_review: self).all.sort_by { |alert| alert[:details][:decisionDate] }
  end

  # Alert types for HLRs and SCs. Only includes the post_decision alert
  def claim_review_alerts
    [
      post_decision
    ].compact
  end

  # Appeal alerts are more varied
  def appeal_alerts
    [
      post_decision,
      post_remand_decision,
      post_effectuation,
      evidentiary_period,
      scheduled_hearing
    ].flatten.compact.uniq
  end

  # The only kind of alerts for HLRs and SCs
  def post_decision
    return unless decision_review.api_alerts_show_decision_alert?
    return unless Time.zone.today <= decision_review.due_date_to_appeal_decision
    return if appeal? && Time.zone.today > decision_review.cavc_due_date

    {
      type: "ama_post_decision",
      details:
      {
        decisionDate: decision_review.decision_date_for_api_alert,
        availableOptions: decision_review.available_review_options,
        dueDate: decision_review.due_date_to_appeal_decision,
        cavcDueDate: appeal? ? (decision_review.decision_date_for_api_alert + 120.days) : nil
      }
    }
  end

  # Example of a different kind of alert. This one is hearing related
  def scheduled_hearing
    return unless decision_review.hearing_docket?

    scheduled_hearing = decision_review.scheduled_hearing
    return unless scheduled_hearing

    {
      type: "scheduled_hearing",
      details: {
        date: scheduled_hearing.scheduled_for.to_date,
        type: decision_review.api_scheduled_hearing_type
      }
    }
  end
```

### `issues`

#### Attribute description
Uses the normal issue serializer to serialize all active request issues or decision issues on the review

#### Possible values
`object` or `hash`: It's an object of objects for all active issues on the review. The total possible combination of values is very large since it reuses the normal IssueSerializer
The attributes on the issue serializer is listed below in the code snippets. These chain into some of the methods listed above for some of the other attribute fields as well like the `api_status_description`


#### Code snippets
```ruby
    def issues(object)
      IssueSerializer.new(object.active_request_issues_or_decision_issues, is_collection: true)
        .serializable_hash[:data].collect { |issue| issue[:attributes] }
    end
  
  # Issue serializer attributes
  attribute :active, &:api_status_active?
  attribute :last_action, &:api_status_last_action
  attribute :date, &:api_status_last_action_date
  attribute :description, &:api_status_description
  attribute :diagnostic_code, &:diagnostic_code

  # Legacy appeal issues are generated by the AppealSeriesIssues rather than the Issues serializer
  def issues
    @issues ||= AppealSeriesIssues.new(appeal_series: self).all
  end
```

#### Last action Issue attribute
The last action attribute is the disposition of the issue for HLRs, SCs, and Appeals.  It is resolved by the `api_status_last_action` method for all 3 types.
For request issues the return value is always nil
```ruby!
  def api_status_last_action
    # this will be nil
    # may need to be updated if an issue is withdrawn
  end
```

For decision issues the disposition is returned or "remand" if it was a "remanded disposition"
```ruby!
  def api_status_last_action
    return "remand" if disposition == "remanded"

    disposition
  end
```

The disposition list for Appeals. It comes from the `client/constants/ISSUE_DISPOSITIONS_BY_ID.json` json file
```json
{
    "allowed": "Allowed",
    "remanded": "Remanded",
    "denied": "Denied",
    "vacated": "Vacated",
    "dismissed_death": "Dismissed, Death",
    "dismissed_matter_of_law": "Dismissed, Matter of Law",
    "withdrawn": "Withdrawn",
    "stayed": "Stayed"
}
```

The disposition list for HLRs and SCs through Caseflow
```javascript!
export const DISPOSITION_OPTIONS = ['Granted', 'Denied', 'DTA Error', 'Dismissed', 'Withdrawn'];
```

LegacyAppeals is a bit more difficult since the data comes out of vacols but the issues last action type should have to match one of the values from this hash in the AppealsSeriesIssues model
```ruby!
  LAST_ACTION_TYPE_FOR_DISPOSITIONS = {
    allowed: [
      :allowed
    ],
    denied: [
      :denied
    ],
    remand: [
      :remanded,
      :manlincon_remand
    ],
    field_grant: [
      :benefits_granted_by_aoj,
      :advance_allowed_in_field
    ],
    withdrawn: [
      :withdrawn,
      :motion_to_vacate_withdrawn,
      :withdrawn_from_remand,
      :recon_motion_withdrawn,
      :advance_withdrawn_by_appellant_rep,
      :advance_failure_to_respond,
      :remand_failure_to_respond,
      :ramp_opt_in
    ]
  }.freeze
```

The one exception to that is that :cavc_remand is also a possible type added by the method itself under certain conditions
```ruby!
  def last_action_for_issues(issues)
    issues.reduce(date: nil, type: nil) do |memo, issue|
      if issue.close_date && (memo[:date].nil? || issue.close_date > memo[:date])
        type = last_action_type_from_disposition(issue.disposition)

        if type
          # Prevent draft decisions from being shared publicly
          unless [:allowed, :denied, :remand].include?(type) && issue.appeal.activated?
            memo[:date] = issue.close_date
            memo[:type] = type
          end
        end
      end

      last_cavc_remand = issue.cavc_decisions.select(&:remanded?).max_by(&:decision_date)

      if last_cavc_remand && (memo[:date].nil? || last_cavc_remand.decision_date > memo[:date])
        memo[:date] = last_cavc_remand.decision_date
        memo[:type] = :cavc_remand
      end

      memo
    end
  end
```
##### Possible values for HLRs and SCs should be 
```ruby!
['Granted', 'Denied', 'DTA Error', 'Dismissed', 'Withdrawn']
```


##### Possible values for Appeals should be
```ruby!
["allowed", "remanded", "denied", "vacated", "dismissed_death", "dismissed_matter_of_law", "withdrawn", "stayed"]
```

##### Possible values for legacy appeals should be
```ruby!
[:allowed,
 :denied,
 :remanded,
 :manlincon_remand,
 :benefits_granted_by_aoj,
 :advance_allowed_in_field,
 :withdrawn,
 :motion_to_vacate_withdrawn,
 :withdrawn_from_remand,
 :recon_motion_withdrawn,
 :advance_withdrawn_by_appellant_rep,
 :advance_failure_to_respond,
 :remand_failure_to_respond,
 :ramp_opt_in,
 :cavc_remand]
```

### `docket`
#### Attribute description
An attribute that contains docket information based on the type of docket, whether or not it's eligible for docket switch, and receipt date of the Appeal

**Special Note: This attribute only applies to Appeal and Legacy Appeals not to HLRs or SCs**

#### Possible values
`object`: An object that contains docket information described above and below

#### Code snippets
```ruby
  # For Appeals
  def docket_hash
    return unless active_status?
    return if location == "aoj"

    {
      type: fetch_docket_type,
      month: Date.parse(receipt_date.to_s).change(day: 1),
      switchDueDate: docket_switch_deadline,
      eligibleToSwitch: eligible_to_switch_dockets?
    }
  end

  # For legacy appeals it is built a little differently based on data retrieved from vacols
  def fetch_docket
    return unless active? && %w[original post_remand].include?(type_code) && form9_date && !aod

    DocketSnapshot.latest.docket_tracer_for_form9_date(form9_date)
  end

  # It should eventually end up looking like these DocketTracer hashes
  def to_hash
    {
      front: at_front,
      total: docket_count,
      ahead: ahead_count,
      ready: ahead_and_ready_count,
      month: month,
      docketMonth: latest_docket_month,
      eta: nil
    }
  end
```


### `events`
#### Attribute description
Generated by the `AppealEvents` model, differing for each decision review.

#### Possible values
`array`: An array of objects generated by the AppealEvents class

#### Code snippets
```ruby
  def events
    @events ||= AppealEvents.new(appeal: self).all
  end

  # HLR events for example
  def hlr_events
    [
      hlr_request_event,
      hlr_decision_event,
      hlr_dta_error_event,
      dta_decision_event,
      hlr_other_close_event
    ].flatten.uniq.select(&:valid?)
  end

  def hlr_decision_event
    AppealEvent.new(type: :hlr_decision, date: appeal.decision_event_date)
  end

  # The appeal event to_hash method
  def to_hash
    { type: type, date: date.to_date }
  end

  # So the resulting value would look like for one of the hlr events 
  { type: :hlr_decision, date: appeal.decision_event_date }

  
```
### `evidence`

#### Attribute description
Always an empty array; a stubbed method.

#### Possible values
`array` always an empty array

#### Code snippets
```ruby
  attribute :evidence do
    []
  end
```


### `type`
#### Attribute description
The type attribute only exists on Appeals and LegacyAppeals. It is the steam type of the appeal or 'Original'. The values are pulled from the `client/constants/AMA_STREAM_TYPES.json` file for appeals.

It is comes from these values for LegacyAppeals
```ruby
  TYPE_CODES = {
    "Original" => "original",
    "Post Remand" => "post_remand",
    "Reconsideration" => "reconsideration",
    "Court Remand" => "post_cavc_remand",
    "Clear and Unmistakable Error" => "cue"
  }.freeze
```

#### Possible values
**Appeals:**
`string`: ["Original", "Vacate", "De Novo", "Court Remand"]
**Legacy Appeals:**
`string`: ["original", "post_remand", "reconsideration", "post_cavc_remand", "cue", "other"]


#### Code snippets
```ruby
  enum stream_type: {
    Constants.AMA_STREAM_TYPES.original.to_sym => Constants.AMA_STREAM_TYPES.original,
    Constants.AMA_STREAM_TYPES.vacate.to_sym => Constants.AMA_STREAM_TYPES.vacate,
    Constants.AMA_STREAM_TYPES.de_novo.to_sym => Constants.AMA_STREAM_TYPES.de_novo,
    Constants.AMA_STREAM_TYPES.court_remand.to_sym => Constants.AMA_STREAM_TYPES.court_remand
  }

  def type
    stream_type&.titlecase || "Original"
  end

{
  "original": "original",
  "vacate": "vacate",
  "de_novo": "de_novo",
  "court_remand": "court_remand"
}

# For legacy appeals
  # Codes for Appeals Status API
  TYPE_CODES = {
    "Original" => "original",
    "Post Remand" => "post_remand",
    "Reconsideration" => "reconsideration",
    "Court Remand" => "post_cavc_remand",
    "Clear and Unmistakable Error" => "cue"
  }.freeze

  def type_code
    TYPE_CODES[type] || "other"
  end
```

## Additional Considerations
This document serves as a structured reference for API field definitions and their implementations. Some fields (e.g., `events`, `alerts`, `status` details) may require further exploration depending on use cases.
