---
title: Cratis Chronicle event copying to another event source
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/events/copying.mdx
description: Copy event history to another event source in Cratis Chronicle with a durable job that appends new events and leaves the source history unchanged.
lastUpdated: 2026-11-05T00:00:00.000Z
---

Copying in Cratis Chronicle seeds one event source with history from another event source in the same event sequence. Use it to create a variant of an entity under a different identifier without changing the source history.

Each copy is a new event with a new sequence number. Observers process these appends, so review their effects before starting: copying history can trigger application actions as well as build state. The source and target identifiers must differ.

## Copying every event

Pass the source and target event source identifiers to copy all event types for the source:

**C#**

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;

public class CopyAccountEventsService(IEventLog eventLog)
{
    public Task<Guid> CopyAccountEvents(EventSourceId sourceId, EventSourceId targetId) =>
        eventLog.Copy(sourceId, targetId);
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/events/copying/by-event-source.md)

**Kotlin**

```kotlin
import io.cratis.chronicle.IEventStore

/**
 * Copies every event of one event source onto another. The copy runs as a durable job on the
 * kernel; the returned job id identifies it.
 */
suspend fun copyAccountHistory(store: IEventStore, sourceId: String, targetId: String): String =
    store.eventLog.copyForEventSource(sourceId, targetId)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/events/copying/by-event-source.md)

**Java**

```java
import io.cratis.chronicle.EventStore;

import java.util.List;

import io.cratis.chronicle.java.EventLogJavaBridge;

class EventsCopyingByEventSource {
    // Copies every event of one event source onto another. The copy runs as a durable job on the
    // kernel; the returned job id identifies it.
    String copyAccountHistory(EventStore store, String sourceId, String targetId) {
        return EventLogJavaBridge.copyForEventSource(store.getEventLog(), sourceId, targetId, List.of());
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/events/copying/by-event-source.md)

**Elixir**

```elixir
defmodule MyApp.CopyByEventSourceService do
  alias Chronicle.EventSequences.EventLog

  def copy_account(source_id, target_id) do
    {:ok, job_id} = EventLog.copy(source_id, target_id)
    job_id
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/events/copying/by-event-source.md)

**TypeScript**

```typescript
import { Guid, IEventLog } from '@cratis/chronicle';

class CopyByEventSourceService {
    constructor(private readonly eventLog: IEventLog) {}

    copyAccount(sourceId: string, targetId: string): Promise<Guid> {
        return this.eventLog.copy(sourceId, targetId);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/events/copying/by-event-source.md)

## Copying specific event types

Provide an event type filter when the target needs only part of the history. Events of other types remain in the source and are not copied:

**C#**

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;

[EventType]
public record AccountOpened(string Name);

[EventType]
public record AccountRenamed(string Name);

public class CopyAccountOpeningsService(IEventLog eventLog)
{
    public Task<Guid> CopyAccountOpenings(EventSourceId sourceId, EventSourceId targetId) =>
        eventLog.Copy(sourceId, targetId, typeof(AccountOpened), typeof(AccountRenamed));
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/events/copying/by-event-source-and-types.md)

**Kotlin**

```kotlin
import io.cratis.chronicle.IEventStore
import io.cratis.chronicle.events.EventType

@EventType
data class CopyingAccountOpened(val owner: String = "")

@EventType
data class CopyingAddressChanged(val street: String = "", val city: String = "")

/**
 * Copies only the given event types for an event source, leaving every other event type behind.
 */
suspend fun copyAccountDetails(store: IEventStore, sourceId: String, targetId: String): String =
    store.eventLog.copyForEventSource(
        sourceId,
        targetId,
        listOf(CopyingAccountOpened::class, CopyingAddressChanged::class)
    )
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/events/copying/by-event-source-and-types.md)

**Java**

```java
import io.cratis.chronicle.EventStore;
import io.cratis.chronicle.events.EventType;

import java.util.List;

import io.cratis.chronicle.java.EventLogJavaBridge;

@EventType
record CopyingAccountOpened(String owner) {}

@EventType
record CopyingAddressChanged(String street, String city) {}

class EventsCopyingByEventSourceAndTypes {
    // Copies only the given event types for an event source, leaving every other event type behind.
    String copyAccountDetails(EventStore store, String sourceId, String targetId) {
        return EventLogJavaBridge.copyForEventSource(
            store.getEventLog(),
            sourceId,
            targetId,
            List.of(CopyingAccountOpened.class, CopyingAddressChanged.class));
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/events/copying/by-event-source-and-types.md)

**Elixir**

```elixir
defmodule MyApp.Events.CopyPersonalDetailsRecorded do
  use Chronicle.Events.EventType, id: "copy-personal-details-recorded"

  defstruct [:name, :social_security_number]
end

defmodule MyApp.Events.CopyAddressChanged do
  use Chronicle.Events.EventType, id: "copy-address-changed"

  defstruct [:street, :city]
end

defmodule MyApp.CopyByEventSourceAndTypesService do
  alias Chronicle.EventSequences.EventLog
  alias MyApp.Events.{CopyAddressChanged, CopyPersonalDetailsRecorded}

  def copy_personal_data(source_id, target_id) do
    {:ok, job_id} =
      EventLog.copy(source_id, target_id, [CopyPersonalDetailsRecorded, CopyAddressChanged])

    job_id
  end
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/events/copying/by-event-source-and-types.md)

**TypeScript**

```typescript
import { eventType, Guid, IEventLog } from '@cratis/chronicle';
import { field } from '@cratis/fundamentals';

@eventType()
class CopyAccountOpened {
    @field(String) readonly name: string;

    constructor(name: string = '') {
        this.name = name;
    }
}

@eventType()
class CopyAddressChanged {
    @field(String) readonly street: string;
    @field(String) readonly city: string;

    constructor(street: string = '', city: string = '') {
        this.street = street;
        this.city = city;
    }
}

class CopyByEventSourceAndTypesService {
    constructor(private readonly eventLog: IEventLog) {}

    copyAccountDetails(sourceId: string, targetId: string): Promise<Guid> {
        return this.eventLog.copy(sourceId, targetId, CopyAccountOpened, CopyAddressChanged);
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/events/copying/by-event-source-and-types.md)

## How it works

The call starts a durable job and returns its identifier, not a completed copy. The job reads matching source events in sequence order and appends them to the target in batches. Follow progress on the Workbench's Jobs page or through the jobs service.

Each copy uses an idempotency key derived from the job identifier and the source event's sequence number. Resuming that job reuses those keys to avoid duplicate appends. Starting a new copy job creates different keys; it does not deduplicate against an earlier job.

A copy keeps the event type and generation, tags, stream, and original occurred time. Its new audit context identifies the copy operation. Personal data is released from the source and protected again for the target. When the subject was the source event source, the target becomes the subject; an explicitly assigned subject stays unchanged.

Copying is not atomic across the whole history. If a target append fails, for example because of a unique constraint, the job reports the failure and earlier batches remain in the target. Inspect the job and target history before deciding how to recover. Use [Getting events](/chronicle/events/getting-events/) to read the resulting history.
