# 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');
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.graphlit.dev/api-guides/use-cases/content/content-lifecycle-states.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
