> For the complete documentation index, see [llms.txt](https://docs.graphlit.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graphlit.dev/api-guides/use-cases/content/content-lifecycle-states.md).

# Lifecycle States

## Content: Lifecycle States

### User Intent

"What do content states mean? When should I use each state?"

### Operation

* **SDK Method**: `updateContent()` with `state` parameter
* **GraphQL Field**: `content.state` (EntityState enum)
* **Entity Type**: Content
* **Common Use Cases**: Content management, soft delete, archival, workflow states

### Content States Overview

Content progresses through states during its lifecycle. Understanding states is critical for content management and querying.

### TypeScript (Canonical)

```typescript
import { Graphlit } from 'graphlit-client';
import { EntityState } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

// Check content state
const content = await graphlit.getContent('content-id');
console.log(`State: ${content.content.state}`);

// Update content state
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled  // Hide from queries
});

// Query only enabled content
const activeContent = await graphlit.queryContents({
  
    states: [EntityState.Enabled]
  });

// Query disabled content (hidden)
const hiddenContent = await graphlit.queryContents({
  
    states: [EntityState.Disabled]
  });

// Query all states (including disabled)
const allContent = await graphlit.queryContents({
  
    states: [
      EntityState.Enabled,
      EntityState.Disabled,
      EntityState.Created
    ]
  });
```

### Entity States

```typescript
enum EntityState {
  CREATED = 'CREATED',       // Just ingested, not yet processed
  ENABLED = 'ENABLED',       // Fully processed, searchable, active
  DISABLED = 'DISABLED',     // Hidden from default queries (soft delete)
  DELETED = 'DELETED',       // Permanently deleted (rarely seen)
  ARCHIVED = 'ARCHIVED'      // Preserved but not actively used
}
```

#### CREATED

* **When**: Immediately after ingestion
* **Duration**: Brief (during workflow processing)
* **Searchable**: No (processing not complete)
* **Use Case**: Content is being prepared/extracted

**Example**:

```typescript
const content = await graphlit.ingestUri(
  'https://example.com/document.pdf',
  undefined,
  undefined,
  undefined,
  false,  // Returns immediately
  { id: 'workflow-id' }
);

console.log(content.ingestUri.state);  // CREATED

// Wait for processing
let isDone = false;
while (!isDone) {
  const status = await graphlit.isContentDone(content.ingestUri.id);
  isDone = status.isContentDone.result;
  await new Promise(resolve => setTimeout(resolve, 1000));
}

// Check state after processing
const processed = await graphlit.getContent(content.ingestUri.id);
console.log(processed.content.state);  // ENABLED
```

#### ENABLED

* **When**: After successful workflow processing
* **Searchable**: Yes (default state for queries)
* **Use Case**: Active, searchable content

**Example**:

```typescript
// Default queries only return ENABLED content
const results = await graphlit.queryContents({
  search: "graphlit"
});

// All results will have state = ENABLED
results.contents.results.forEach(content => {
  console.log(content.state);  // ENABLED
});
```

#### DISABLED

* **When**: Manually disabled by user
* **Searchable**: No (unless explicitly queried)
* **Use Case**: Soft delete, hide temporarily, compliance hold

**Example**:

```typescript
// Disable content (soft delete)
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

// Default queries won't return it
const results = await graphlit.queryContents({
  search: "query"
});
// Content with DISABLED state won't appear

// Must explicitly query disabled content
const disabled = await graphlit.queryContents({
  
    states: [EntityState.Disabled]
  });
```

#### DELETED

* **When**: Permanently deleted via deleteContent()
* **Searchable**: No (content is gone)
* **Use Case**: Permanent removal

**Example**:

```typescript
// Permanent delete
await graphlit.deleteContent('content-id');

// Content no longer exists
// Can't query or retrieve
```

#### ARCHIVED

* **When**: Manually archived by user
* **Searchable**: No (unless explicitly queried)
* **Use Case**: Long-term retention, compliance, not actively used

**Example**:

```typescript
// Archive content
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Archived
});

// Query archived content
const archived = await graphlit.queryContents({
  
    states: [EntityState.Archived]
  });
```

### State Transitions

```
Ingest → CREATED
  ↓
Workflow Processing
  ↓
ENABLED ←→ DISABLED (soft delete/restore)
  ↓
ARCHIVED (long-term storage)
  ↓
DELETED (permanent removal)
```

**Valid Transitions**:

* `CREATED` → `ENABLED` (automatic after processing)
* `ENABLED` → `DISABLED` (soft delete)
* `DISABLED` → `ENABLED` (restore)
* `ENABLED` → `ARCHIVED` (archive)
* `ARCHIVED` → `ENABLED` (restore from archive)
* Any → `DELETED` (permanent delete)

## Check state

content = await graphlit.getContent('content-id') print(f"State: {content.content.state}")

## Update state (snake\_case)

await graphlit.updateContent( id='content-id', state=EntityState.DISABLED )

## Query by state

results = await graphlit.queryContents( filter=ContentFilterInput( states=\[EntityState.ENABLED] ) )

````

**C#**:
```csharp
using Graphlit;

var client = new Graphlit();

// Check state
var content = await graphlit.GetContent("content-id");
Console.WriteLine($"State: {content.Content.State}");

// Update state (PascalCase)
await graphlit.UpdateContent(new ContentUpdateInput
{
    Id = "content-id",
    State = EntityState.Disabled
});

// Query by state
var results = await graphlit.QueryContents(new ContentFilter
{
    States = new[] { EntityState.Enabled }
});
````

### Developer Hints

#### Default Query Behavior

```typescript
// This query...
const results = await graphlit.queryContents({
  search: "query"
});

// ...is equivalent to:
const results = await graphlit.queryContents({
  search: "query",
  
    states: [EntityState.Enabled]  // Default!
  });

// DISABLED, ARCHIVED content won't appear unless explicitly queried
```

#### Soft Delete vs Hard Delete

```typescript
// Soft delete (reversible)
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});

// Can restore later
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Enabled
});

// Hard delete (permanent)
await graphlit.deleteContent('content-id');
// Content is gone forever
```

#### Querying Multiple States

```typescript
// Query both enabled and archived
const results = await graphlit.queryContents({
  
    states: [
      EntityState.Enabled,
      EntityState.Archived
    ]
  });

// Query everything except deleted
const everything = await graphlit.queryContents({
  
    states: [
      EntityState.Created,
      EntityState.Enabled,
      EntityState.Disabled,
      EntityState.Archived
    ]
  });
```

### Variations

#### 1. Soft Delete Content

```typescript
// Hide from queries but preserve
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});
```

#### 2. Restore Soft-Deleted Content

```typescript
// Make searchable again
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Enabled
});
```

#### 3. Archive Old Content

```typescript
// Archive content older than 1 year
const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);

const oldContent = await graphlit.queryContents({
  
    creationDateRange: {
      to: oneYearAgo.toISOString()
    },
    states: [EntityState.Enabled]
  });

// Archive each
for (const content of oldContent.contents.results) {
  await graphlit.updateContent({
    id: content.id,
    state: EntityState.Archived
  });
  console.log(`Archived: ${content.name}`);
}
```

#### 4. Query Only Active Content

```typescript
// Explicit enabled filter (best practice)
const active = await graphlit.queryContents({
  
    states: [EntityState.Enabled]
  });
```

#### 5. View Disabled Content (Admin View)

```typescript
// Show hidden content
const hidden = await graphlit.queryContents({
  
    states: [EntityState.Disabled]
  });

console.log(`${hidden.contents.results.length} hidden items`);
```

#### 6. Bulk State Changes

```typescript
// Disable multiple content items
const contentIds = ['id1', 'id2', 'id3'];

for (const id of contentIds) {
  await graphlit.updateContent({
    id,
    state: EntityState.Disabled
  });
}

// Or use batch operations
await Promise.all(
  contentIds.map(id =>
    graphlit.updateContent({
      id,
      state: EntityState.Disabled
    })
  )
);
```

#### 7. Compliance Archival

```typescript
// Archive but preserve for compliance
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Archived,
  metadata: {
    archivedDate: new Date().toISOString(),
    archivedReason: 'Compliance retention',
    retainUntil: '2030-01-01'
  }
});
```

### Common Issues & Solutions

**Issue**: Content not appearing in queries **Solution**: Check if content is disabled or archived

```typescript
// Check content state
const content = await graphlit.getContent('content-id');
console.log(`State: ${content.content.state}`);

if (content.content.state !== EntityState.Enabled) {
  console.log('Content is not enabled');
}
```

**Issue**: Accidentally deleted content **Solution**: Use soft delete (DISABLED) instead

```typescript
//  DON'T - Permanent delete
await graphlit.deleteContent('content-id');

//  DO - Soft delete
await graphlit.updateContent({
  id: 'content-id',
  state: EntityState.Disabled
});
```

**Issue**: Archived content still appearing **Solution**: Default queries only return ENABLED

```typescript
// This won't return archived content
const results = await graphlit.queryContents({});

// Must explicitly exclude if needed
const onlyActive = await graphlit.queryContents({
  
    states: [EntityState.Enabled]
  });
```

**Issue**: Content stuck in CREATED state **Solution**: Check workflow processing status

```typescript
const content = await graphlit.getContent('content-id');

if (content.content.state === EntityState.Created) {
  // Check if processing is done
  const status = await graphlit.isContentDone('content-id');
  
  if (!status.isContentDone.result) {
    console.log('Still processing...');
  } else if (content.content.error) {
    console.log(`Error: ${content.content.error}`);
  }
}
```

### Production Example

```typescript
async function manageContentLifecycle(contentId: string) {
  const content = await graphlit.getContent(contentId);
  
  console.log(`\n=== CONTENT LIFECYCLE ===`);
  console.log(`ID: ${content.content.id}`);
  console.log(`Name: ${content.content.name}`);
  console.log(`Current State: ${content.content.state}`);
  console.log(`Created: ${content.content.creationDate}`);
  
  // State-based actions
  switch (content.content.state) {
    case EntityState.Created:
      console.log('\n⏳ Processing...');
      const isDone = await graphlit.isContentDone(contentId);
      if (isDone.isContentDone.result) {
        console.log('✓ Processing complete');
      } else {
        console.log('Still processing workflow');
      }
      break;
      
    case EntityState.Enabled:
      console.log('\n✓ Active and searchable');
      
      // Check if old enough to archive
      const age = Date.now() - new Date(content.content.creationDate).getTime();
      const daysOld = age / (1000 * 60 * 60 * 24);
      
      if (daysOld > 365) {
        console.log(`📦 Content is ${Math.floor(daysOld)} days old - consider archiving`);
      }
      break;
      
    case EntityState.Disabled:
      console.log('\n🔒 Hidden (soft deleted)');
      console.log('Can be restored with state: ENABLED');
      break;
      
    case EntityState.Archived:
      console.log('\n📦 Archived');
      console.log('Preserved for compliance, not actively used');
      break;
  }
  
  return content.content.state;
}

// Usage
await manageContentLifecycle('content-id');
```
