# Writing Events

You write to a stream over HTTP using a POST request to the resource of the stream. If the stream does not exist then the stream is implicitly created.

# EventStoreDB media types

EventStoreDB supports a custom media type for posting events, application/vnd.eventstore.events+json or application/vnd.eventstore.events+xml. This format allows for extra functionality that posting events as application/json or application/xml does not. For example it allows you to post multiple events in a single batch.

The format represents data with the following jschema (eventId must be a UUID).

[
    {
      "eventId"    : "string",
      "eventType"  : "string",
      "data"       : "object",
      "metadata"   : "object"
    }
]

# Writing a single event

If you issue a POST request with data to a stream and the correct content type set it writes the event to the stream, and generates a 201 response from the server, giving you the location of the event. Using the following event, which you can also download as a file:

[
  {
    "eventId": "fbf4a1a1-b4a3-4dfe-a01f-ec52c34e16e4",
    "eventType": "event-type",
    "data": {
      "a": "1"
    }
  }
]

POST the following request to create a stream and add an event to it:

    curl -i -d "@event.json" "http://127.0.0.1:2113/streams/newstream" \
        -H "Content-Type:application/vnd.eventstore.events+json"
        -u "admin:changeit"
    HTTP/1.1 201 Created
    Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
    Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
    Access-Control-Allow-Origin: *
    Location: http://127.0.0.1:2113/streams/newstream/0
    Content-Type: text/plain; charset: utf-8
    Server: Mono-HTTPAPI/1.0
    Date: Fri, 28 Jun 2013 12:17:59 GMT
    Content-Length: 0
    Keep-Alive: timeout=15,max=100
    // Make sure to add code blocks to your code group

    Some clients may not be able to generate a unique identifier (or may not want to) for the event ID. You need this ID for idempotence purposes and EventStoreDB can generate it for you.

    If you leave off the ES-EventId header you see different behavior:

      curl -i -d "@event.json" "http://127.0.0.1:2113/streams/newstream" \
          -H "Content-Type:application/json" \
          -H "ES-EventType: SomeEvent"
      HTTP/1.1 307 Temporary Redirect
      Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
      Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
      Access-Control-Allow-Origin: *
      Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
      Location: http://127.0.0.1:2113/streams/newstream/incoming/8a00e469-3a99-4517-a0b0-8dc662ffad9b
      Content-Type: text/plain; charset=utf-8
      Server: Mono-HTTPAPI/1.0
      Date: Tue, 24 Jul 2018 14:42:44 GMT
      Content-Length: 28
      Keep-Alive: timeout=15,max=100
      Forwarding to idempotent URI%
      // Make sure to add code blocks to your code group

      In this case EventStoreDB has responded with a 307 Temporary Redirect. The location points to another URI that you can post the event to. This new URI is idempotent for posting, even without an event ID.

        curl -i -d "@event.json" "http://127.0.0.1:2113/streams/newstream/incoming/8a00e469-3a99-4517-a0b0-8dc662ffad9b" \
            -H "Content-Type: application/json" -H "ES-EventType: SomeEvent"
        HTTP/1.1 201 Created
        Access-Control-Allow-Methods: GET, POST, OPTIONS
        Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
        Access-Control-Allow-Origin: *
        Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
        Location: http://127.0.0.1:2113/streams/newstream/0
        Content-Type: text/plain; charset=utf-8
        Server: Mono-HTTPAPI/1.0
        Date: Tue, 24 Jul 2018 14:46:10 GMT
        Content-Length: 0
        Keep-Alive: timeout=15,max=100
        // Make sure to add code blocks to your code group

        It's generally recommended to include an event ID if possible as it results in fewer round trips between the client and the server.

        When posting to either the stream or to the returned redirect, clients must include the EventType header. If you forget to include the header you receive an error.

          curl -i -d "@event.json" "http://127.0.0.1:2113/streams/newstream" -H "Content-Type:application/json"
          HTTP/1.1 400 Must include an event type with the request either in body or as ES-EventType header.
          Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
          Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
          Access-Control-Allow-Origin: *
          Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
          Content-Type:
          Server: Mono-HTTPAPI/1.0
          Date: Tue, 24 Jul 2018 14:50:59 GMT
          Content-Length: 0
          Connection: close
          // Make sure to add code blocks to your code group

          # Batch writes

          You can include more than one write in a single post by placing multiple events inside of the array representing the events, including metadata.

          For example, the below has two events:

          [
              {
                  "eventId": "fbf4b1a1-b4a3-4dfe-a01f-ec52c34e16e4",
                  "eventType": "event-type",
                  "data": {
                      "a": "1"
                  }
              },
              {
                  "eventId": "0f9fad5b-d9cb-469f-a165-70867728951e",
                  "eventType": "event-type",
                  "data": {
                      "b": "2"
                  }
              }
          ]

          When you write multiple events in a single post, EventStoreDB treats them as one transaction, it writes all events together or fails.

            curl -i -d "@multiple-events.json" "http://127.0.0.1:2113/streams/newstream" \
                -H "Content-Type:application/vnd.eventstore.events+json"
            HTTP/1.1 201 Created
            Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
            Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
            Access-Control-Allow-Origin: *
            Location: http://127.0.0.1:2113/streams/newstream/1
            Content-Type: text/plain; charset: utf-8
            Server: Mono-HTTPAPI/1.0
            Date: Fri, 28 Jun 2013 12:32:18 GMT
            Content-Length: 0
            Keep-Alive: timeout=15,max=100
            // Make sure to add code blocks to your code group

            # Appending events

            To append events, issue a POST request to the same resource with a new eventId:

            [
                {
                    "eventId": "fbf4a1a1-b4a3-4dfe-a01f-ec52c34e16e5",
                    "eventType": "event-type",
                    "data": {
                        "b": "2"
                    }
                }
            ]
              curl -i -d "@event-append.json" "http://127.0.0.1:2113/streams/newstream" \
                  -H "Content-Type:application/vnd.eventstore.events+json" \
                  -H "ES-EventType: SomeEvent"
              HTTP/1.1 201 Created
              Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
              Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER
              Access-Control-Allow-Origin: *
              Location: http://127.0.0.1:2113/streams/newstream/1
              Content-Type: text/plain; charset: utf-8
              Server: Mono-HTTPAPI/1.0
              Date: Fri, 28 Jun 2013 12:32:18 GMT
              Content-Length: 0
              Keep-Alive: timeout=15,max=100
              // Make sure to add code blocks to your code group

              # Data-only events

              Version 3.7.0 of EventStoreDB added support for the application/octet-stream content type to support data-only binary events. When creating these events, you need to provide the ES-EventType and ES-EventId headers and cannot have metadata associated with the event. In the example below SGVsbG8gV29ybGQ= is the data you POST to the stream:

                curl -i -d "SGVsbG8gV29ybGQ=" "http://127.0.0.1:2113/streams/newstream" \
                    -H "Content-Type:application/octet-stream" \
                    -H "ES-EventType:rawDataType" \
                    -H "ES-EventId:eeccf3ce-4f54-409d-8870-b35dd836cca6"
                HTTP/1.1 201 Created
                Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
                Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
                Access-Control-Allow-Origin: *
                Access-Control-Expose-Headers: Location, ES-Position
                Location: http://127.0.0.1:2113/streams/newstream/0
                Content-Type: text/plain; charset=utf-8
                Server: Mono-HTTPAPI/1.0
                Date: Mon, 27 Jun 2016 13:15:27 GMT
                Content-Length: 0
                Keep-Alive: timeout=15,max=100
                // Make sure to add code blocks to your code group

                # Expected version header

                The expected version header represents the version of the stream you expect.

                For example if you write to a stream at version 1, then you expect it to be at version 1 next time you write. This can allow for optimistic locking when multiple applications are reading/writing to streams.

                If your expected version is not the current version you receive an HTTP status code of 400.

                WARNING

                See the idempotence section below, if you post the same event twice it is idempotent and won't return a version error.

                First write an event to a stream, setting a version:

                [
                    {
                        "eventId": "fbf4b1a1-b4a3-4dfe-a01f-ec52c34e16e4",
                        "eventType": "event-type",
                        "data": {
                            "a": "1"
                        }
                    }
                ]
                  curl -i -d @event-version.json "http://127.0.0.1:2113/streams/newstream" \
                      -H "Content-Type:application/vnd.eventstore.events+json" \
                      -H "ES-CurrentVersion: 0"
                  HTTP/1.1 201 Created
                  Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
                  Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
                  Access-Control-Allow-Origin: *
                  Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
                  Location: http://127.0.0.1:2113/streams/newstream/2
                  Content-Type: text/plain; charset=utf-8
                  Server: Mono-HTTPAPI/1.0
                  Date: Tue, 14 Aug 2018 10:02:08 GMT
                  Content-Length: 0
                  Keep-Alive: timeout=15,max=100
                  // Make sure to add code blocks to your code group

                  If you now write to the stream with the incorrect version, you receive an HTTP status code 400 error.

                    curl -i -d @event-version.json "http://127.0.0.1:2113/streams/newstream" \
                        -H "Content-Type:application/vnd.eventstore.events+json" \
                        -H "ES-ExpectedVersion: 3"
                    HTTP/1.1 400 Wrong expected EventNumber
                    Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
                    Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
                    Access-Control-Allow-Origin: *
                    Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
                    ES-CurrentVersion: 0
                    Content-Type: text/plain; charset=utf-8
                    Server: Mono-HTTPAPI/1.0
                    Date: Tue, 14 Aug 2018 14:08:44 GMT
                    Content-Length: 0
                    Connection: close
                    // Make sure to add code blocks to your code group

                    There are special values you can use in the expected version header:

                    • -2 states that this write should never conflict and should always succeed.
                    • -1 states that the stream should not exist at the time of the writing (this write creates it).
                    • 0 states that the stream should exist but should be empty.

                    # Idempotence

                    Appends to streams are idempotent based upon the EventId assigned in your post. If you were to re-run the last command it returns the same value again.

                    This is important behaviour as it's how you implement error handling. If you receive a timeout, broken connection, no answer, etc from your HTTP POST then it's your responsibility to retry the post. You must also keep the same UUID that you assigned to the event in the first POST.

                    If you are using the expected version parameter with your post, then EventStoreDB is 100% idempotent. If you use -2 as your expected version value, EventStoreDB does its best to keep events idempotent but cannot assure that everything is fully idempotent and you end up in 'at-least-once' messaging. Read this guide for more details on idempotence.

                    Last Updated: 1/1/2021, 11:29:55 AM