diff --git a/config/dev.exs b/config/dev.exs
index 41fb0f730..b438a25b3 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -50,6 +50,8 @@ config :mobilizon, MobilizonWeb.Endpoint,
 # Do not include metadata nor timestamps in development logs
 config :logger, :console, format: "[$level] $message\n", level: :debug
 
+config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Nominatim
+
 # Set a higher stacktrace during development. Avoid configuring such
 # in production as building large stacktraces may be expensive.
 config :phoenix, :stacktrace_depth, 20
diff --git a/js/src/components/Event/AddressAutoComplete.vue b/js/src/components/Event/AddressAutoComplete.vue
new file mode 100644
index 000000000..023ef7963
--- /dev/null
+++ b/js/src/components/Event/AddressAutoComplete.vue
@@ -0,0 +1,53 @@
+<template>
+    <b-field label="Find an address">
+        <b-autocomplete
+                :data="data"
+                placeholder="e.g. 10 Rue Jangot"
+                field="description"
+                :loading="isFetching"
+                @typing="getAsyncData"
+                @select="option => selected = option">
+
+            <template slot-scope="{option}">
+                <b>{{ option.description }}</b>
+                <p>
+                    <small>{{ option.street }}</small>,&#32;
+                    <small>{{ option.locality }}</small>
+                </p>
+            </template>
+        </b-autocomplete>
+    </b-field>
+</template>
+<script lang="ts">
+import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
+import { IAddress } from '@/types/address.model';
+import { ADDRESS } from '@/graphql/address';
+@Component
+export default class AddressAutoComplete extends Vue {
+
+  @Prop({ required: false, default: () => [] }) initialData!: IAddress[];
+
+  data: IAddress[] = this.initialData;
+  selected: IAddress|null = null;
+  isFetching: boolean = false;
+
+  async getAsyncData(query) {
+    if (!query.length) {
+      this.data = [];
+      return;
+    }
+    this.isFetching = true;
+    const result = await this.$apollo.query({
+      query: ADDRESS,
+      variables: { query },
+    });
+
+    this.data = result.data.searchAddress as IAddress[];
+  }
+
+  @Watch("selected")
+  updateSelected() {
+    this.$emit('input', this.selected);
+  }
+}
+</script>
diff --git a/js/src/graphql/address.ts b/js/src/graphql/address.ts
new file mode 100644
index 000000000..0a16a98de
--- /dev/null
+++ b/js/src/graphql/address.ts
@@ -0,0 +1,18 @@
+import gql from 'graphql-tag';
+
+export const ADDRESS = gql`
+    query($query:String!) {
+        searchAddress(
+            query: $query
+        ) {
+            description,
+            geom,
+            floor,
+            street,
+            locality,
+            postalCode,
+            region,
+            country
+        }
+    }
+`;
diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts
index a7174dcb5..48ad5381f 100644
--- a/js/src/graphql/event.ts
+++ b/js/src/graphql/event.ts
@@ -144,7 +144,8 @@ export const CREATE_EVENT = gql`
   $category: String!,
   $beginsOn: DateTime!,
   $picture: PictureInput,
-  $tags: [String]
+  $tags: [String],
+  $physicalAddress: AddressInput!
   ) {
     createEvent(
       title: $title,
@@ -153,7 +154,8 @@ export const CREATE_EVENT = gql`
       organizerActorId: $organizerActorId,
       category: $category,
       picture: $picture,
-      tags: $tags
+      tags: $tags,
+      physicalAddress: $physicalAddress
     ) {
       id,
       uuid,
diff --git a/js/src/types/address.model.ts b/js/src/types/address.model.ts
index 663ec425f..fddab2695 100644
--- a/js/src/types/address.model.ts
+++ b/js/src/types/address.model.ts
@@ -1,4 +1,5 @@
 export interface IAddress {
+  id: number;
   description: string;
   floor: string;
   street: string;
diff --git a/js/src/views/Event/Create.vue b/js/src/views/Event/Create.vue
index 4c0ce83e5..856432c05 100644
--- a/js/src/views/Event/Create.vue
+++ b/js/src/views/Event/Create.vue
@@ -14,6 +14,8 @@
 
         <tag-input v-model="event.tags" :data="tags" path="title" />
 
+        <address-auto-complete v-model="event.physicalAddress" />
+
         <date-time-picker v-model="event.beginsOn" :label="$gettext('Starts on…')" :step="15"/>
         <date-time-picker v-model="event.endsOn" :label="$gettext('Ends on…')" :step="15" />
 
@@ -57,9 +59,10 @@ import DateTimePicker from '@/components/Event/DateTimePicker.vue';
 import TagInput from '@/components/Event/TagInput.vue';
 import { TAGS } from '@/graphql/tags';
 import { ITag } from '@/types/tag.model';
+import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
 
 @Component({
-  components: { TagInput, DateTimePicker, PictureUpload, Editor },
+  components: { AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor },
   apollo: {
     loggedPerson: {
       query: LOGGED_PERSON,
@@ -134,9 +137,13 @@ export default class CreateEvent extends Vue {
     const obj = {
       organizerActorId: this.loggedPerson.id,
       beginsOn: this.event.beginsOn.toISOString(),
-      tags: this.event.tags.map((tag: ITag) => tag.title),
+      tags: this.event.tags.map((tag: ITag) => tag.title)
     };
-    const res = Object.assign({}, this.event, obj);
+    let res = Object.assign({}, this.event, obj);
+
+    if (this.event.physicalAddress) {
+      delete this.event.physicalAddress['__typename'];
+    }
 
     /**
      * Transform picture files
diff --git a/lib/mix/tasks/mobilizon/toot.ex b/lib/mix/tasks/mobilizon/toot.ex
index e3ae95747..3f9371eb0 100644
--- a/lib/mix/tasks/mobilizon/toot.ex
+++ b/lib/mix/tasks/mobilizon/toot.ex
@@ -11,10 +11,10 @@ defmodule Mix.Tasks.Mobilizon.Toot do
     Mix.Task.run("app.start")
 
     case MobilizonWeb.API.Comments.create_comment(from, content) do
-      {:ok, _} ->
+      {:ok, _, _} ->
         Mix.shell().info("Tooted")
 
-      {:local_actor, _} ->
+      {:local_actor, _, _} ->
         Mix.shell().error("Failed to toot.\nActor #{from} doesn't exist")
 
       _ ->
diff --git a/lib/mobilizon/addresses/address.ex b/lib/mobilizon/addresses/address.ex
index 405a0af92..6dfcb434a 100644
--- a/lib/mobilizon/addresses/address.ex
+++ b/lib/mobilizon/addresses/address.ex
@@ -6,6 +6,20 @@ defmodule Mobilizon.Addresses.Address do
   alias Mobilizon.Addresses.Address
   alias Mobilizon.Events.Event
   # alias Mobilizon.Actors.Actor
+  @attrs [
+    :description,
+    :floor,
+    :geom,
+    :country,
+    :locality,
+    :region,
+    :postal_code,
+    :street,
+    :url
+  ]
+  @required [
+    :url
+  ]
 
   schema "addresses" do
     field(:country, :string)
@@ -16,8 +30,8 @@ defmodule Mobilizon.Addresses.Address do
     field(:geom, Geo.PostGIS.Geometry)
     field(:postal_code, :string)
     field(:street, :string)
-    has_one(:event, Event, foreign_key: :physical_address_id)
-    # has_one(:group, Actor)
+    field(:url, :string)
+    has_many(:event, Event, foreign_key: :physical_address_id)
 
     timestamps()
   end
@@ -25,15 +39,15 @@ defmodule Mobilizon.Addresses.Address do
   @doc false
   def changeset(%Address{} = address, attrs) do
     address
-    |> cast(attrs, [
-      :description,
-      :floor,
-      :geom,
-      :country,
-      :locality,
-      :region,
-      :postal_code,
-      :street
-    ])
+    |> cast(attrs, @attrs)
+    |> set_url()
+    |> validate_required(@required)
+  end
+
+  defp set_url(%Ecto.Changeset{changes: changes} = changeset) do
+    url =
+      Map.get(changes, :url, MobilizonWeb.Endpoint.url() <> "/address/#{Ecto.UUID.generate()}")
+
+    put_change(changeset, :url, url)
   end
 end
diff --git a/lib/mobilizon/addresses/addresses.ex b/lib/mobilizon/addresses/addresses.ex
index a0c8c0022..0c2304f41 100644
--- a/lib/mobilizon/addresses/addresses.ex
+++ b/lib/mobilizon/addresses/addresses.ex
@@ -50,6 +50,21 @@ defmodule Mobilizon.Addresses do
   """
   def get_address!(id), do: Repo.get!(Address, id)
 
+  @doc """
+  Gets a single address by it's url
+
+  ## Examples
+
+      iex> get_address_by_url("https://mobilizon.social/addresses/4572")
+      %Address{}
+
+      iex> get_address_by_url("https://mobilizon.social/addresses/099")
+      nil
+  """
+  def get_address_by_url(url) do
+    Repo.get_by(Address, url: url)
+  end
+
   @doc """
   Creates a address.
 
@@ -163,7 +178,7 @@ defmodule Mobilizon.Addresses do
   We only look at the description for now, and eventually order by object distance
   """
   @spec search_addresses(String.t(), list()) :: list(Address.t())
-  def search_addresses(search, options) do
+  def search_addresses(search, options \\ []) do
     limit = Keyword.get(options, :limit, 5)
 
     query = from(a in Address, where: ilike(a.description, ^"%#{search}%"), limit: ^limit)
@@ -181,7 +196,7 @@ defmodule Mobilizon.Addresses do
         do: from(a in query, where: ilike(a.country, ^"%#{country}%")),
         else: query
 
-    Repo.all(query)
+    if Keyword.get(options, :single, false) == true, do: Repo.one(query), else: Repo.all(query)
   end
 
   @doc """
diff --git a/lib/mobilizon/events/event.ex b/lib/mobilizon/events/event.ex
index 95005bb72..fe100b3ea 100644
--- a/lib/mobilizon/events/event.ex
+++ b/lib/mobilizon/events/event.ex
@@ -84,9 +84,9 @@ defmodule Mobilizon.Events.Event do
       :online_address,
       :phone_address,
       :uuid,
-      :picture_id
+      :picture_id,
+      :physical_address_id
     ])
-    |> cast_assoc(:physical_address)
     |> validate_required([
       :title,
       :begins_on,
diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex
index 31908182d..7194dc89e 100644
--- a/lib/mobilizon/events/events.ex
+++ b/lib/mobilizon/events/events.ex
@@ -382,7 +382,8 @@ defmodule Mobilizon.Events do
 
   defp do_create_event(attrs) do
     with {:ok, %Event{} = event} <- %Event{} |> Event.changeset(attrs) |> Repo.insert(),
-         %Event{} = event <- event |> Repo.preload([:tags, :organizer_actor]),
+         %Event{} = event <-
+           event |> Repo.preload([:tags, :organizer_actor, :physical_address, :picture]),
          {:has_tags, true, _} <- {:has_tags, Map.has_key?(attrs, "tags"), event} do
       event
       |> Ecto.Changeset.change()
@@ -513,8 +514,10 @@ defmodule Mobilizon.Events do
   @doc """
   Get an existing tag or create one
   """
-  @spec get_or_create_tag(String.t()) :: {:ok, Tag.t()} | {:error, any()}
-  def get_or_create_tag(title) do
+  @spec get_or_create_tag(map()) :: {:ok, Tag.t()} | {:error, any()}
+  def get_or_create_tag(tag) do
+    "#" <> title = tag["name"]
+
     case Repo.get_by(Tag, title: title) do
       %Tag{} = tag ->
         {:ok, tag}
@@ -1223,9 +1226,13 @@ defmodule Mobilizon.Events do
 
   """
   def create_comment(attrs \\ %{}) do
-    %Comment{}
-    |> Comment.changeset(attrs)
-    |> Repo.insert()
+    with {:ok, %Comment{} = comment} <-
+           %Comment{}
+           |> Comment.changeset(attrs)
+           |> Repo.insert(),
+         %Comment{} = comment <- Repo.preload(comment, [:actor, :in_reply_to_comment]) do
+      {:ok, comment}
+    end
   end
 
   @doc """
diff --git a/lib/mobilizon_web/api/comments.ex b/lib/mobilizon_web/api/comments.ex
index 6680bd420..fbef71418 100644
--- a/lib/mobilizon_web/api/comments.ex
+++ b/lib/mobilizon_web/api/comments.ex
@@ -15,7 +15,8 @@ defmodule MobilizonWeb.API.Comments do
 
   Creates a comment from an actor and a status
   """
-  @spec create_comment(String.t(), String.t(), String.t()) :: {:ok, Activity.t()} | any()
+  @spec create_comment(String.t(), String.t(), String.t()) ::
+          {:ok, Activity.t(), Comment.t()} | any()
   def create_comment(
         from_username,
         status,
diff --git a/lib/mobilizon_web/api/events.ex b/lib/mobilizon_web/api/events.ex
index aface5cf2..4207dcd4a 100644
--- a/lib/mobilizon_web/api/events.ex
+++ b/lib/mobilizon_web/api/events.ex
@@ -2,6 +2,7 @@ defmodule MobilizonWeb.API.Events do
   @moduledoc """
   API for Events
   """
+  alias Mobilizon.Addresses
   alias Mobilizon.Actors
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Service.ActivityPub
@@ -11,7 +12,7 @@ defmodule MobilizonWeb.API.Events do
   @doc """
   Create an event
   """
-  @spec create_event(map()) :: {:ok, Activity.t()} | any()
+  @spec create_event(map()) :: {:ok, Activity.t(), Event.t()} | any()
   def create_event(
         %{
           title: title,
@@ -22,10 +23,9 @@ defmodule MobilizonWeb.API.Events do
           tags: tags
         } = args
       ) do
-    require Logger
-
     with %Actor{url: url} = actor <-
            Actors.get_local_actor_with_everything(organizer_actor_id),
+         physical_address <- Map.get(args, :physical_address, nil),
          title <- String.trim(title),
          visibility <- Map.get(args, :visibility, :public),
          picture <- Map.get(args, :picture, nil),
@@ -34,14 +34,12 @@ defmodule MobilizonWeb.API.Events do
          event <-
            ActivityPubUtils.make_event_data(
              url,
-             to,
+             %{to: to, cc: cc},
              title,
              content_html,
              picture,
              tags,
-             cc,
-             %{begins_on: begins_on},
-             category
+             %{begins_on: begins_on, physical_address: physical_address, category: category}
            ) do
       ActivityPub.create(%{
         to: ["https://www.w3.org/ns/activitystreams#Public"],
@@ -51,4 +49,15 @@ defmodule MobilizonWeb.API.Events do
       })
     end
   end
+
+  defp get_physical_address(address_id) when is_number(address_id),
+    do: Addresses.get_address!(address_id)
+
+  defp get_physical_address(address_id) when is_binary(address_id) do
+    with {address_id, ""} <- Integer.parse(address_id) do
+      get_physical_address(address_id)
+    end
+  end
+
+  defp get_physical_address(nil), do: nil
 end
diff --git a/lib/mobilizon_web/api/groups.ex b/lib/mobilizon_web/api/groups.ex
index 7daf2aa7b..8c6e8f64b 100644
--- a/lib/mobilizon_web/api/groups.ex
+++ b/lib/mobilizon_web/api/groups.ex
@@ -11,7 +11,7 @@ defmodule MobilizonWeb.API.Groups do
   @doc """
   Create a group
   """
-  @spec create_group(map()) :: {:ok, Activity.t()} | any()
+  @spec create_group(map()) :: {:ok, Activity.t(), Group.t()} | any()
   def create_group(
         %{
           preferred_username: title,
diff --git a/lib/mobilizon_web/resolvers/address.ex b/lib/mobilizon_web/resolvers/address.ex
index 3dabf4c9a..372cc03b9 100644
--- a/lib/mobilizon_web/resolvers/address.ex
+++ b/lib/mobilizon_web/resolvers/address.ex
@@ -11,7 +11,7 @@ defmodule MobilizonWeb.Resolvers.Address do
   Search an address
   """
   @spec search(map(), map(), map()) :: {:ok, list(Address.t())}
-  def search(_parent, %{query: query}, %{context: %{ip: ip}}) do
+  def search(_parent, %{query: query, page: _page, limit: _limit}, %{context: %{ip: ip}}) do
     country = Geolix.lookup(ip) |> Map.get(:country, nil)
 
     local_addresses = Task.async(fn -> Addresses.search_addresses(query, country: country) end)
diff --git a/lib/mobilizon_web/resolvers/comment.ex b/lib/mobilizon_web/resolvers/comment.ex
index 09d866bf3..c167b898f 100644
--- a/lib/mobilizon_web/resolvers/comment.ex
+++ b/lib/mobilizon_web/resolvers/comment.ex
@@ -11,14 +11,10 @@ defmodule MobilizonWeb.Resolvers.Comment do
   def create_comment(_parent, %{text: comment, actor_username: username}, %{
         context: %{current_user: %User{} = _user}
       }) do
-    with {:ok, %Activity{data: %{"object" => %{"type" => "Note"} = object}}} <-
+    with {:ok, %Activity{data: %{"object" => %{"type" => "Note"} = _object}},
+          %Comment{} = comment} <-
            Comments.create_comment(username, comment) do
-      {:ok,
-       %Comment{
-         text: object["content"],
-         url: object["id"],
-         uuid: object["uuid"]
-       }}
+      {:ok, comment}
     end
   end
 
diff --git a/lib/mobilizon_web/resolvers/event.ex b/lib/mobilizon_web/resolvers/event.ex
index 339fbbdbc..d3155bed5 100644
--- a/lib/mobilizon_web/resolvers/event.ex
+++ b/lib/mobilizon_web/resolvers/event.ex
@@ -3,6 +3,8 @@ defmodule MobilizonWeb.Resolvers.Event do
   Handles the event-related GraphQL calls
   """
   alias Mobilizon.Activity
+  alias Mobilizon.Addresses
+  alias Mobilizon.Addresses.Address
   alias Mobilizon.Events
   alias Mobilizon.Events.{Event, Participant}
   alias Mobilizon.Media.Picture
@@ -190,25 +192,10 @@ defmodule MobilizonWeb.Resolvers.Event do
   """
   def create_event(_parent, args, %{context: %{current_user: _user}} = _resolution) do
     with {:ok, args} <- save_attached_picture(args),
-         {:ok, %Activity{data: %{"object" => %{"type" => "Event"} = object}}} <-
+         {:ok, args} <- save_physical_address(args),
+         {:ok, %Activity{data: %{"object" => %{"type" => "Event"} = _object}}, %Event{} = event} <-
            MobilizonWeb.API.Events.create_event(args) do
-      res = %{
-        title: object["name"],
-        description: object["content"],
-        uuid: object["uuid"],
-        url: object["id"]
-      }
-
-      res =
-        if Map.has_key?(object, "attachment"),
-          do:
-            Map.put(res, :picture, %{
-              name: object["attachment"] |> hd() |> Map.get("name"),
-              url: object["attachment"] |> hd() |> Map.get("url") |> hd() |> Map.get("href")
-            }),
-          else: res
-
-      {:ok, res}
+      {:ok, event}
     end
   end
 
@@ -237,6 +224,25 @@ defmodule MobilizonWeb.Resolvers.Event do
   @spec save_attached_picture(map()) :: {:ok, map()}
   defp save_attached_picture(args), do: {:ok, args}
 
+  @spec save_physical_address(map()) :: {:ok, map()}
+  defp save_physical_address(%{physical_address: %{url: physical_address_url}} = args) do
+    with %Address{} = address <- Addresses.get_address_by_url(physical_address_url),
+         args <- Map.put(args, :physical_address, address) do
+      {:ok, args}
+    end
+  end
+
+  #  @spec save_physical_address(map()) :: {:ok, map()}
+  #  defp save_physical_address(%{physical_address: address} = args) do
+  #    with {:ok, %Address{} = address} <- Addresses.create_address(address),
+  #         args <- Map.put(args, :physical_address, address) do
+  #      {:ok, args}
+  #    end
+  #  end
+
+  @spec save_physical_address(map()) :: {:ok, map()}
+  defp save_physical_address(args), do: {:ok, args}
+
   @doc """
   Delete an event
   """
diff --git a/lib/mobilizon_web/resolvers/group.ex b/lib/mobilizon_web/resolvers/group.ex
index e24f4ae35..96b4214eb 100644
--- a/lib/mobilizon_web/resolvers/group.ex
+++ b/lib/mobilizon_web/resolvers/group.ex
@@ -47,20 +47,15 @@ defmodule MobilizonWeb.Resolvers.Group do
            :ok,
            %Activity{
              data: %{
-               "object" => %{"type" => "Group"} = object
+               "object" => %{"type" => "Group"} = _object
              }
-           }
+           },
+           %Actor{} = group
          } <-
            MobilizonWeb.API.Groups.create_group(args) do
       {
         :ok,
-        %Actor{
-          preferred_username: object["preferredUsername"],
-          summary: object["summary"],
-          type: :Group,
-          #  uuid: object["uuid"],
-          url: object["id"]
-        }
+        group
       }
     end
 
diff --git a/lib/mobilizon_web/schema/address.ex b/lib/mobilizon_web/schema/address.ex
index acbdeed76..2bbbb0bec 100644
--- a/lib/mobilizon_web/schema/address.ex
+++ b/lib/mobilizon_web/schema/address.ex
@@ -14,6 +14,7 @@ defmodule MobilizonWeb.Schema.AddressType do
     field(:region, :string)
     field(:country, :string)
     field(:description, :string)
+    field(:url, :string)
   end
 
   object :phone_address do
@@ -26,10 +27,25 @@ defmodule MobilizonWeb.Schema.AddressType do
     field(:info, :string)
   end
 
+  input_object :address_input do
+    # Either a full picture object
+    field(:geom, :point, description: "The geocoordinates for the point where this address is")
+    field(:floor, :string, description: "The floor this event is at")
+    field(:street, :string, description: "The address's street name (with number)")
+    field(:locality, :string, description: "The address's locality")
+    field(:postal_code, :string)
+    field(:region, :string)
+    field(:country, :string)
+    field(:description, :string)
+    field(:url, :string)
+  end
+
   object :address_queries do
     @desc "Search for an address"
     field :search_address, type: list_of(:address) do
       arg(:query, non_null(:string))
+      arg(:page, :integer, default_value: 1)
+      arg(:limit, :integer, default_value: 10)
 
       resolve(&Resolvers.Address.search/3)
     end
diff --git a/lib/mobilizon_web/schema/event.ex b/lib/mobilizon_web/schema/event.ex
index 12285a7a4..aff8bc835 100644
--- a/lib/mobilizon_web/schema/event.ex
+++ b/lib/mobilizon_web/schema/event.ex
@@ -22,7 +22,7 @@ defmodule MobilizonWeb.Schema.EventType do
     field(:begins_on, :datetime, description: "Datetime for when the event begins")
     field(:ends_on, :datetime, description: "Datetime for when the event ends")
     field(:status, :event_status, description: "Status of the event")
-    field(:visibility, :event_visibility, description: "The event's visibility")
+    field(:visibility, :event_visibility, description: "The event's  visibility")
 
     field(:picture, :picture,
       description: "The event's picture",
@@ -132,6 +132,7 @@ defmodule MobilizonWeb.Schema.EventType do
       arg(:phone_address, :string)
       arg(:organizer_actor_id, non_null(:id))
       arg(:category, non_null(:string))
+      arg(:physical_address, :address_input)
 
       resolve(&Event.create_event/3)
     end
diff --git a/lib/service/activity_pub/activity_pub.ex b/lib/service/activity_pub/activity_pub.ex
index 0efdc5dcf..483a7a14f 100644
--- a/lib/service/activity_pub/activity_pub.ex
+++ b/lib/service/activity_pub/activity_pub.ex
@@ -70,10 +70,11 @@ defmodule Mobilizon.Service.ActivityPub do
   def fetch_object_from_url(url) do
     Logger.info("Fetching object from url #{url}")
 
-    with true <- String.starts_with?(url, "http"),
-         nil <- Events.get_event_by_url(url),
-         nil <- Events.get_comment_from_url(url),
-         {:error, :actor_not_found} <- Actors.get_actor_by_url(url),
+    with {:not_http, true} <- {:not_http, String.starts_with?(url, "http")},
+         {:existing_event, nil} <- {:existing_event, Events.get_event_by_url(url)},
+         {:existing_comment, nil} <- {:existing_comment, Events.get_comment_from_url(url)},
+         {:existing_actor, {:error, :actor_not_found}} <-
+           {:existing_actor, Actors.get_actor_by_url(url)},
          {:ok, %{body: body, status_code: code}} when code in 200..299 <-
            HTTPoison.get(
              url,
@@ -90,25 +91,32 @@ defmodule Mobilizon.Service.ActivityPub do
            "actor" => data["attributedTo"],
            "object" => data
          },
-         {:ok, activity} <- Transmogrifier.handle_incoming(params) do
+         {:ok, _activity, %{url: object_url} = _object} <- Transmogrifier.handle_incoming(params) do
       case data["type"] do
         "Event" ->
-          {:ok, Events.get_event_by_url!(activity.data["object"]["id"])}
+          {:ok, Events.get_event_by_url!(object_url)}
 
         "Note" ->
-          {:ok, Events.get_comment_full_from_url!(activity.data["object"]["id"])}
+          {:ok, Events.get_comment_full_from_url!(object_url)}
 
         "Actor" ->
-          {:ok, Actors.get_actor_by_url!(activity.data["object"]["id"], true)}
+          {:ok, Actors.get_actor_by_url!(object_url, true)}
 
         other ->
           {:error, other}
       end
     else
-      %Event{url: event_url} -> {:ok, Events.get_event_by_url!(event_url)}
-      %Comment{url: comment_url} -> {:ok, Events.get_comment_full_from_url!(comment_url)}
-      %Actor{url: actor_url} -> {:ok, Actors.get_actor_by_url!(actor_url, true)}
-      e -> {:error, e}
+      {:existing_event, %Event{url: event_url}} ->
+        {:ok, Events.get_event_by_url!(event_url)}
+
+      {:existing_comment, %Comment{url: comment_url}} ->
+        {:ok, Events.get_comment_full_from_url!(comment_url)}
+
+      {:existing_actor, %Actor{url: actor_url}} ->
+        {:ok, Actors.get_actor_by_url!(actor_url, true)}
+
+      e ->
+        {:error, e}
     end
   end
 
@@ -130,10 +138,10 @@ defmodule Mobilizon.Service.ActivityPub do
              additional
            ),
          :ok <- Logger.debug(inspect(create_data)),
-         {:ok, activity, _object} <- insert(create_data, local),
+         {:ok, activity, object} <- insert(create_data, local),
          :ok <- maybe_federate(activity) do
       # {:ok, actor} <- Actors.increase_event_count(actor) do
-      {:ok, activity}
+      {:ok, activity, object}
     else
       err ->
         Logger.error("Something went wrong")
@@ -147,9 +155,9 @@ defmodule Mobilizon.Service.ActivityPub do
     local = !(params[:local] == false)
 
     with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object},
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     end
   end
 
@@ -164,9 +172,9 @@ defmodule Mobilizon.Service.ActivityPub do
            "actor" => actor,
            "object" => object
          },
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     end
   end
 
@@ -179,7 +187,7 @@ defmodule Mobilizon.Service.ActivityPub do
   #     ) do
   #   with nil <- get_existing_like(url, object),
   #        like_data <- make_like_data(user, object, activity_id),
-  #        {:ok, activity, _object} <- insert(like_data, local),
+  #        {:ok, activity, object} <- insert(like_data, local),
   #        {:ok, object} <- add_like_to_object(activity, object),
   #        :ok <- maybe_federate(activity) do
   #     {:ok, activity, object}
@@ -215,7 +223,7 @@ defmodule Mobilizon.Service.ActivityPub do
   #     ) do
   #   #with true <- is_public?(object),
   #        with announce_data <- make_announce_data(actor, object, activity_id),
-  #        {:ok, activity, _object} <- insert(announce_data, local),
+  #        {:ok, activity, object} <- insert(announce_data, local),
   #       #  {:ok, object} <- add_announce_to_object(activity, object),
   #        :ok <- maybe_federate(activity) do
   #     {:ok, activity, object}
@@ -250,9 +258,9 @@ defmodule Mobilizon.Service.ActivityPub do
          activity_follow_id <-
            activity_id || "#{MobilizonWeb.Endpoint.url()}/follow/#{follow_id}/activity",
          data <- make_follow_data(followed, follower, activity_follow_id),
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     else
       {err, _} when err in [:already_following, :suspended] ->
         {:error, err}
@@ -269,9 +277,9 @@ defmodule Mobilizon.Service.ActivityPub do
          data <- make_follow_data(followed, follower, follow_id),
          {:ok, follow_activity, _object} <- insert(data, local),
          unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
-         {:ok, activity, _object} <- insert(unfollow_data, local),
+         {:ok, activity, object} <- insert(unfollow_data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     else
       err ->
         Logger.error(inspect(err))
@@ -290,9 +298,9 @@ defmodule Mobilizon.Service.ActivityPub do
     }
 
     with {:ok, _} <- Events.delete_event(event),
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     end
   end
 
@@ -305,9 +313,9 @@ defmodule Mobilizon.Service.ActivityPub do
     }
 
     with {:ok, _} <- Events.delete_comment(comment),
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     end
   end
 
@@ -320,9 +328,9 @@ defmodule Mobilizon.Service.ActivityPub do
     }
 
     with {:ok, _} <- Actors.delete_actor(actor),
-         {:ok, activity, _object} <- insert(data, local),
+         {:ok, activity, object} <- insert(data, local),
          :ok <- maybe_federate(activity) do
-      {:ok, activity}
+      {:ok, activity, object}
     end
   end
 
diff --git a/lib/service/activity_pub/converters/address.ex b/lib/service/activity_pub/converters/address.ex
new file mode 100644
index 000000000..d6d0cc1c3
--- /dev/null
+++ b/lib/service/activity_pub/converters/address.ex
@@ -0,0 +1,58 @@
+defmodule Mobilizon.Service.ActivityPub.Converters.Address do
+  @moduledoc """
+  Flag converter
+
+  This module allows to convert reports from ActivityStream format to our own internal one, and back.
+
+  Note: Reports are named Flag in AS.
+  """
+  alias Mobilizon.Addresses.Address, as: AddressModel
+  alias Mobilizon.Service.ActivityPub.Converter
+
+  @behaviour Converter
+
+  @doc """
+  Converts an AP object data to our internal data structure
+  """
+  @impl Converter
+  @spec as_to_model_data(map()) :: map()
+  def as_to_model_data(object) do
+    res = %{
+      "description" => object["name"],
+      "url" => object["url"]
+    }
+
+    res =
+      if is_nil(object["address"]) do
+        res
+      else
+        Map.merge(res, %{
+          "country" => object["address"]["addressCountry"],
+          "postal_code" => object["address"]["postalCode"],
+          "region" => object["address"]["addressRegion"],
+          "street" => object["address"]["streetAddress"],
+          "locality" => object["address"]["addressLocality"]
+        })
+      end
+
+    if is_nil(object["geo"]) do
+      res
+    else
+      geo = %Geo.Point{
+        coordinates: {object["geo"]["latitude"], object["geo"]["longitude"]},
+        srid: 4326
+      }
+
+      Map.put(res, "geom", geo)
+    end
+  end
+
+  @doc """
+  Convert an event struct to an ActivityStream representation
+  """
+  @impl Converter
+  @spec model_to_as(AddressModel.t()) :: map()
+  def model_to_as(%AddressModel{} = _address) do
+    nil
+  end
+end
diff --git a/lib/service/activity_pub/converters/event.ex b/lib/service/activity_pub/converters/event.ex
index 64021611a..1c6a64500 100644
--- a/lib/service/activity_pub/converters/event.ex
+++ b/lib/service/activity_pub/converters/event.ex
@@ -12,19 +12,28 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Event do
   alias Mobilizon.Service.ActivityPub.Converter
   alias Mobilizon.Events
   alias Mobilizon.Events.Tag
+  alias Mobilizon.Addresses
+  alias Mobilizon.Addresses.Address
 
   @behaviour Converter
 
+  require Logger
+
   @doc """
   Converts an AP object data to our internal data structure
   """
   @impl Converter
   @spec as_to_model_data(map()) :: map()
   def as_to_model_data(object) do
-    with {:ok, %Actor{id: actor_id}} <- Actors.get_actor_by_url(object["actor"]),
-         tags <- fetch_tags(object["tag"]) do
+    Logger.debug("event as_to_model_data")
+
+    with {:actor, {:ok, %Actor{id: actor_id}}} <-
+           {:actor, Actors.get_actor_by_url(object["actor"])},
+         {:address, address_id} <-
+           {:address, get_address(object["location"])},
+         {:tags, tags} <- {:tags, fetch_tags(object["tag"])} do
       picture_id =
-        with true <- Map.has_key?(object, "attachment"),
+        with true <- Map.has_key?(object, "attachment") && length(object["attachment"]) > 0,
              %Picture{id: picture_id} <-
                Media.get_picture_by_url(
                  object["attachment"]
@@ -38,27 +47,64 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Event do
           _ -> nil
         end
 
-      %{
-        "title" => object["name"],
-        "description" => object["content"],
-        "organizer_actor_id" => actor_id,
-        "picture_id" => picture_id,
-        "begins_on" => object["begins_on"],
-        "category" => object["category"],
-        "url" => object["id"],
-        "uuid" => object["uuid"],
-        "tags" => tags
-      }
+      {:ok,
+       %{
+         "title" => object["name"],
+         "description" => object["content"],
+         "organizer_actor_id" => actor_id,
+         "picture_id" => picture_id,
+         "begins_on" => object["startTime"],
+         "category" => object["category"],
+         "url" => object["id"],
+         "uuid" => object["uuid"],
+         "tags" => tags,
+         "physical_address_id" => address_id
+       }}
+    else
+      err ->
+        {:error, err}
+    end
+  end
+
+  defp get_address(%{"id" => url} = map) when is_map(map) and is_binary(url) do
+    Logger.debug("Address with an URL, let's check against our own database")
+
+    case Addresses.get_address_by_url(url) do
+      %Address{id: address_id} ->
+        address_id
+
+      _ ->
+        Logger.debug("not in our database, let's try to create it")
+        map = Map.put(map, "url", map["id"])
+        do_get_address(map)
+    end
+  end
+
+  defp get_address(map) when is_map(map) do
+    do_get_address(map)
+  end
+
+  defp get_address(nil), do: nil
+
+  defp do_get_address(map) do
+    map = Mobilizon.Service.ActivityPub.Converters.Address.as_to_model_data(map)
+
+    case Addresses.create_address(map) do
+      {:ok, %Address{id: address_id}} ->
+        address_id
+
+      _ ->
+        nil
     end
   end
 
   defp fetch_tags(tags) do
     Enum.reduce(tags, [], fn tag, acc ->
-      case Events.get_or_create_tag(tag) do
-        {:ok, %Tag{} = tag} ->
-          acc ++ [tag]
-
-        _ ->
+      with true <- tag["type"] == "Hashtag",
+           {:ok, %Tag{} = tag} <- Events.get_or_create_tag(tag) do
+        acc ++ [tag]
+      else
+        _err ->
           acc
       end
     end)
diff --git a/lib/service/activity_pub/transmogrifier.ex b/lib/service/activity_pub/transmogrifier.ex
index 5f4658b37..d2022b78f 100644
--- a/lib/service/activity_pub/transmogrifier.ex
+++ b/lib/service/activity_pub/transmogrifier.ex
@@ -132,9 +132,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
     end
   end
 
-  # TODO: validate those with a Ecto scheme
-  # - tags
-  # - emoji
   def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
     Logger.info("Handle incoming to create notes")
 
@@ -159,15 +156,39 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
     end
   end
 
+  def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Event"} = object} = data) do
+    Logger.info("Handle incoming to create event")
+
+    with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(data["actor"]) do
+      Logger.debug("found actor")
+      Logger.debug(inspect(actor))
+
+      params = %{
+        to: data["to"],
+        object: object |> fix_object,
+        actor: actor,
+        local: false,
+        published: data["published"],
+        additional:
+          Map.take(data, [
+            "cc",
+            "id"
+          ])
+      }
+
+      ActivityPub.create(params)
+    end
+  end
+
   def handle_incoming(
         %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
       ) do
     with {:ok, %Actor{} = followed} <- Actors.get_or_fetch_by_url(followed, true),
          {:ok, %Actor{} = follower} <- Actors.get_or_fetch_by_url(follower),
-         {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
+         {:ok, activity, object} <- ActivityPub.follow(follower, followed, id, false) do
       ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true})
 
-      {:ok, activity}
+      {:ok, activity, object}
     else
       e ->
         Logger.error("Unable to handle Follow activity")
@@ -257,9 +278,9 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
       ) do
     with {:ok, %Actor{domain: nil} = followed} <- Actors.get_actor_by_url(followed),
          {:ok, %Actor{} = follower} <- Actors.get_actor_by_url(follower),
-         {:ok, activity} <- ActivityPub.unfollow(followed, follower, id, false) do
+         {:ok, activity, object} <- ActivityPub.unfollow(followed, follower, id, false) do
       Actor.unfollow(follower, followed)
-      {:ok, activity}
+      {:ok, activity, object}
     else
       e ->
         Logger.error(inspect(e))
@@ -282,11 +303,11 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
          {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
          #  TODO : Validate that DELETE comes indeed form right domain (see above)
          #  :ok <- contain_origin(actor_url, object.data),
-         {:ok, activity} <- ActivityPub.delete(object, false) do
-      {:ok, activity}
+         {:ok, activity, object} <- ActivityPub.delete(object, false) do
+      {:ok, activity, object}
     else
       e ->
-        Logger.debug(inspect(e))
+        Logger.error(inspect(e))
         :error
     end
   end
diff --git a/lib/service/activity_pub/utils.ex b/lib/service/activity_pub/utils.ex
index 8f1b9b5ca..59c0b1c45 100644
--- a/lib/service/activity_pub/utils.ex
+++ b/lib/service/activity_pub/utils.ex
@@ -11,6 +11,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
   """
 
   alias Mobilizon.Repo
+  alias Mobilizon.Addresses.Address
   alias Mobilizon.Actors
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Events.Event
@@ -122,7 +123,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
   """
   def insert_full_object(%{"object" => %{"type" => "Event"} = object_data})
       when is_map(object_data) do
-    with object_data <-
+    with {:ok, object_data} <-
            Converters.Event.as_to_model_data(object_data),
          {:ok, %Event{} = event} <- Events.create_event(object_data) do
       {:ok, event}
@@ -260,26 +261,21 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
   """
   @spec make_event_data(
           String.t(),
-          String.t(),
+          map(),
           String.t(),
           String.t(),
           map(),
           list(),
-          list(),
-          map(),
-          String.t()
+          map()
         ) :: map()
   def make_event_data(
         actor,
-        to,
+        %{to: to, cc: cc} = _audience,
         title,
         content_html,
         picture \\ nil,
         tags \\ [],
-        # _cw \\ nil,
-        cc \\ [],
-        metadata \\ %{},
-        category \\ ""
+        metadata \\ %{}
       ) do
     Logger.debug("Making event data")
     uuid = Ecto.UUID.generate()
@@ -287,21 +283,58 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
     res = %{
       "type" => "Event",
       "to" => to,
-      "cc" => cc,
+      "cc" => cc || [],
       "content" => content_html,
       "name" => title,
-      # "summary" => cw,
-      "begins_on" => metadata.begins_on,
-      "category" => category,
+      "startTime" => metadata.begins_on,
+      "category" => metadata.category,
       "actor" => actor,
       "id" => Routes.page_url(Endpoint, :event, uuid),
       "uuid" => uuid,
-      "tag" => tags |> Enum.uniq()
+      "tag" =>
+        tags |> Enum.uniq() |> Enum.map(fn tag -> %{"type" => "Hashtag", "name" => "##{tag}"} end)
     }
 
+    res =
+      if is_nil(metadata.physical_address),
+        do: res,
+        else: Map.put(res, "location", make_address_data(metadata.physical_address))
+
     if is_nil(picture), do: res, else: Map.put(res, "attachment", [make_picture_data(picture)])
   end
 
+  def make_address_data(%Address{} = address) do
+    res = %{
+      "type" => "Place",
+      "name" => address.description,
+      "id" => address.url,
+      "address" => %{
+        "type" => "PostalAddress",
+        "streetAddress" => address.street,
+        "postalCode" => address.postal_code,
+        "addressLocality" => address.locality,
+        "addressRegion" => address.region,
+        "addressCountry" => address.country
+      }
+    }
+
+    if is_nil(address.geom) do
+      res
+    else
+      Map.put(res, "geo", %{
+        "type" => "GeoCoordinates",
+        "latitude" => address.geom.coordinates |> elem(0),
+        "longitude" => address.geom.coordinates |> elem(1)
+      })
+    end
+  end
+
+  def make_address_data(address) do
+    Address
+    |> struct(address)
+    |> make_address_data()
+  end
+
   @doc """
   Make an AP comment object from an set of values
   """
diff --git a/lib/service/federator.ex b/lib/service/federator.ex
index 6be044bcd..da9d54205 100644
--- a/lib/service/federator.ex
+++ b/lib/service/federator.ex
@@ -52,7 +52,7 @@ defmodule Mobilizon.Service.Federator do
     Logger.debug(inspect(params))
 
     case Transmogrifier.handle_incoming(params) do
-      {:ok, activity} ->
+      {:ok, activity, _} ->
         {:ok, activity}
 
       %Activity{} ->
diff --git a/priv/repo/migrations/20190729125532_add-url-to-addresses.exs b/priv/repo/migrations/20190729125532_add-url-to-addresses.exs
new file mode 100644
index 000000000..41ed31bd6
--- /dev/null
+++ b/priv/repo/migrations/20190729125532_add-url-to-addresses.exs
@@ -0,0 +1,9 @@
+defmodule :"Elixir.Mobilizon.Repo.Migrations.Add-url-to-addresses" do
+  use Ecto.Migration
+
+  def change do
+    alter table(:addresses) do
+      add(:url, :string, null: false)
+    end
+  end
+end
diff --git a/schema.graphql b/schema.graphql
index 57cf761a6..e69b2c165 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -1,5 +1,5 @@
 # source: http://localhost:4000/api
-# timestamp: Fri Jul 26 2019 11:28:32 GMT+0200 (GMT+02:00)
+# timestamp: Mon Jul 29 2019 15:24:10 GMT+0200 (GMT+02:00)
 
 schema {
   query: RootQueryType
@@ -121,6 +121,25 @@ type Address {
   street: String
 }
 
+input AddressInput {
+  country: String
+  description: String
+
+  """The floor this event is at"""
+  floor: String
+
+  """The geocoordinates for the point where this address is"""
+  geom: Point
+
+  """The address's locality"""
+  locality: String
+  postalCode: String
+  region: String
+
+  """The address's street name (with number)"""
+  street: String
+}
+
 """A comment"""
 type Comment {
   """Internal ID for this comment"""
@@ -262,7 +281,7 @@ type Event {
   """The Event UUID"""
   uuid: UUID
 
-  """The event's visibility"""
+  """The event's  visibility"""
   visibility: EventVisibility
 }
 
@@ -675,6 +694,7 @@ type RootMutationType {
     onlineAddress: String
     organizerActorId: ID!
     phoneAddress: String
+    physicalAddress: AddressInput
 
     """
     The picture for the event, either as an object or directly the ID of an existing Picture
@@ -891,7 +911,7 @@ type RootQueryType {
   reverseGeocode(latitude: Float!, longitude: Float!): [Address]
 
   """Search for an address"""
-  searchAddress(query: String!): [Address]
+  searchAddress(limit: Int = 10, page: Int = 1, query: String!): [Address]
 
   """Search events"""
   searchEvents(limit: Int = 10, page: Int = 1, search: String!): Events
diff --git a/test/fixtures/mobilizon-post-activity.json b/test/fixtures/mobilizon-post-activity.json
new file mode 100644
index 000000000..b21f4bfa3
--- /dev/null
+++ b/test/fixtures/mobilizon-post-activity.json
@@ -0,0 +1,66 @@
+{
+    "@context": [
+        "https://www.w3.org/ns/activitystreams",
+        "https://w3id.org/security/v1",
+        {
+            "mblzn": "https://joinmobilizon.org/ns#",
+            "Hashtag": "as:Hashtag",
+            "sc": "http://schema.org#",
+            "Place": "sc:Place",
+            "PostalAddress": "sc:PostalAddress",
+            "uuid": "sc:identifier"
+        }
+    ],
+    "actor": "https://event1.tcit.fr/@tcit",
+    "cc": [
+        "https://framapiaf.org/users/admin/followers",
+        "http://localtesting.pleroma.lol/users/lain"
+    ],
+    "id": "https://event1.tcit.fr/@tcit/events/109ccdfd-ee3e-46e1-a877-6c228763df0c/activity",
+    "object": {
+        "attachment": [],
+        "attributedTo": "https://event1.tcit.fr/@tcit",
+        "startTime": "2018-02-12T14:08:20Z",
+        "cc": [
+            "https://framapiaf.org/users/admin/followers",
+            "http://localtesting.pleroma.lol/users/lain"
+        ],
+        "content": "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>",
+        "category": "TODO remove me",
+        "id": "https://event1.tcit.fr/@tcit/events/109ccdfd-ee3e-46e1-a877-6c228763df0c",
+        "inReplyTo": null,
+        "location": {
+            "type": "Place",
+            "name": "Locaux de Framasoft",
+            "id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd",
+            "address": {
+                "type": "PostalAddress",
+                "streetAddress": "10 Rue Jangot",
+                "postalCode": "69007",
+                "addressLocality": "Lyon",
+                "addressRegion": "Auvergne Rhône Alpes",
+                "addressCountry": "France"
+            }
+        },
+        "name": "My first event",
+        "published": "2018-02-12T14:08:20Z",
+        "tag": [
+            {
+                "href": "http://localtesting.pleroma.lol/users/lain",
+                "name": "@lain@localtesting.pleroma.lol",
+                "type": "Mention"
+            }
+        ],
+        "to": [
+            "https://www.w3.org/ns/activitystreams#Public"
+        ],
+        "type": "Event",
+        "url": "https://event1.tcit.fr/@tcit/events/109ccdfd-ee3e-46e1-a877-6c228763df0c",
+        "uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c"
+    },
+    "published": "2018-02-12T14:08:20Z",
+    "to": [
+        "https://www.w3.org/ns/activitystreams#Public"
+    ],
+    "type": "Create"
+}
diff --git a/test/mobilizon/addresses/addresses_test.exs b/test/mobilizon/addresses/addresses_test.exs
index 04b6f78b1..8f9e267fe 100644
--- a/test/mobilizon/addresses/addresses_test.exs
+++ b/test/mobilizon/addresses/addresses_test.exs
@@ -1,6 +1,7 @@
 defmodule Mobilizon.AddressesTest do
   use Mobilizon.DataCase
 
+  import Mobilizon.Factory
   alias Mobilizon.Addresses
 
   describe "addresses" do
@@ -37,22 +38,13 @@ defmodule Mobilizon.AddressesTest do
     #   geom: nil
     # }
 
-    def address_fixture(attrs \\ %{}) do
-      {:ok, address} =
-        attrs
-        |> Enum.into(@valid_attrs)
-        |> Addresses.create_address()
-
-      address
-    end
-
     test "list_addresses/0 returns all addresses" do
-      address = address_fixture()
+      address = insert(:address)
       assert [address.id] == Addresses.list_addresses() |> Enum.map(& &1.id)
     end
 
     test "get_address!/1 returns the address with given id" do
-      address = address_fixture()
+      address = insert(:address)
       assert Addresses.get_address!(address.id).id == address.id
     end
 
@@ -68,7 +60,7 @@ defmodule Mobilizon.AddressesTest do
     end
 
     test "update_address/2 with valid data updates the address" do
-      address = address_fixture()
+      address = insert(:address)
       assert {:ok, %Address{} = address} = Addresses.update_address(address, @update_attrs)
       assert address.country == "some updated addressCountry"
       assert address.locality == "some updated addressLocality"
@@ -80,13 +72,13 @@ defmodule Mobilizon.AddressesTest do
     end
 
     test "delete_address/1 deletes the address" do
-      address = address_fixture()
+      address = insert(:address)
       assert {:ok, %Address{}} = Addresses.delete_address(address)
       assert_raise Ecto.NoResultsError, fn -> Addresses.get_address!(address.id) end
     end
 
     test "change_address/1 returns a address changeset" do
-      address = address_fixture()
+      address = insert(:address)
       assert %Ecto.Changeset{} = Addresses.change_address(address)
     end
 
diff --git a/test/mobilizon/service/activity_pub/activity_pub_test.exs b/test/mobilizon/service/activity_pub/activity_pub_test.exs
index c452af8d0..12aa5f373 100644
--- a/test/mobilizon/service/activity_pub/activity_pub_test.exs
+++ b/test/mobilizon/service/activity_pub/activity_pub_test.exs
@@ -56,7 +56,7 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
     test "removes doubled 'to' recipients" do
       actor = insert(:actor)
 
-      {:ok, activity} =
+      {:ok, activity, _} =
         ActivityPub.create(%{
           to: ["user1", "user1", "user2"],
           actor: actor,
@@ -113,7 +113,7 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
     test "it creates a delete activity and deletes the original event" do
       event = insert(:event)
       event = Events.get_event_full_by_url!(event.url)
-      {:ok, delete} = ActivityPub.delete(event)
+      {:ok, delete, _} = ActivityPub.delete(event)
 
       assert delete.data["type"] == "Delete"
       assert delete.data["actor"] == event.organizer_actor.url
@@ -125,7 +125,7 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
     test "it creates a delete activity and deletes the original comment" do
       comment = insert(:comment)
       comment = Events.get_comment_full_from_url!(comment.url)
-      {:ok, delete} = ActivityPub.delete(comment)
+      {:ok, delete, _} = ActivityPub.delete(comment)
 
       assert delete.data["type"] == "Delete"
       assert delete.data["actor"] == comment.actor.url
@@ -140,7 +140,7 @@ defmodule Mobilizon.Service.ActivityPub.ActivityPubTest do
       actor = insert(:actor)
       actor_data = MobilizonWeb.ActivityPub.ActorView.render("actor.json", %{actor: actor})
 
-      {:ok, update} =
+      {:ok, update, _} =
         ActivityPub.update(%{
           actor: actor_data["url"],
           to: [actor.url <> "/followers"],
diff --git a/test/mobilizon/service/activity_pub/transmogrifier_test.exs b/test/mobilizon/service/activity_pub/transmogrifier_test.exs
index ffd8b32cf..56e3e296f 100644
--- a/test/mobilizon/service/activity_pub/transmogrifier_test.exs
+++ b/test/mobilizon/service/activity_pub/transmogrifier_test.exs
@@ -12,7 +12,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
   alias Mobilizon.Actors
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Events
-  alias Mobilizon.Events.Comment
+  alias Mobilizon.Events.{Comment, Event}
   alias Mobilizon.Service.ActivityPub.Utils
   alias Mobilizon.Service.ActivityPub.Transmogrifier
   use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
@@ -21,7 +21,51 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     HTTPoison.start()
   end
 
-  describe "handle_incoming" do
+  describe "handle incoming events" do
+    test "it works for incoming events" do
+      data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
+
+      {:ok, %Mobilizon.Activity{data: data, local: false}, %Event{} = event} =
+        Transmogrifier.handle_incoming(data)
+
+      assert data["id"] ==
+               "https://event1.tcit.fr/@tcit/events/109ccdfd-ee3e-46e1-a877-6c228763df0c/activity"
+
+      assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+      #
+      #      assert data["cc"] == [
+      #               "https://framapiaf.org/users/admin/followers",
+      #               "http://localtesting.pleroma.lol/users/lain"
+      #             ]
+
+      assert data["actor"] == "https://event1.tcit.fr/@tcit"
+
+      object = data["object"]
+
+      assert object["id"] ==
+               "https://event1.tcit.fr/@tcit/events/109ccdfd-ee3e-46e1-a877-6c228763df0c"
+
+      assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+
+      #      assert object["cc"] == [
+      #               "https://framapiaf.org/users/admin/followers",
+      #               "http://localtesting.pleroma.lol/users/lain"
+      #             ]
+
+      assert object["actor"] == "https://event1.tcit.fr/@tcit"
+      assert object["location"]["name"] == "Locaux de Framasoft"
+      assert object["attributedTo"] == "https://event1.tcit.fr/@tcit"
+
+      assert event.physical_address.street == "10 Rue Jangot"
+
+      assert event.physical_address.url ==
+               "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd"
+
+      {:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
+    end
+  end
+
+  describe "handle incoming notices" do
     # test "it ignores an incoming comment if we already have it" do
     #   comment = insert(:comment)
 
@@ -37,7 +81,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #     |> Jason.decode!()
     #     |> Map.put("object", activity["object"])
 
-    #   {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
+    #   {:ok, returned_activity, _} = Transmogrifier.handle_incoming(data)
 
     #   assert activity == returned_activity.data
     # end
@@ -55,7 +99,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #     data
     #     |> Map.put("object", object)
 
-    #   {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
+    #   {:ok, returned_activity, _} = Transmogrifier.handle_incoming(data)
 
     #   assert activity =
     #            Activity.get_create_activity_by_object_ap_id(
@@ -71,7 +115,8 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it works for incoming notices" do
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
 
-      {:ok, %Mobilizon.Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Mobilizon.Activity{data: data, local: false}, _} =
+        Transmogrifier.handle_incoming(data)
 
       assert data["id"] == "https://framapiaf.org/users/admin/statuses/99512778738411822/activity"
 
@@ -105,7 +150,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it works for incoming notices with hashtags" do
       data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
       assert Enum.at(data["object"]["tag"], 2) == "moo"
     end
 
@@ -113,7 +158,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #       data =
     #         File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Jason.decode!()
 
-    #       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+    #       {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
     #       assert data["object"]["content"] ==
     #                "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
@@ -122,7 +167,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #     test "it works for incoming notices with to/cc not being an array (kroeg)" do
     #       data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
 
-    #       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+    #       {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
     #       assert data["object"]["content"] ==
     #                "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
@@ -131,7 +176,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #     test "it works for incoming announces with actor being inlined (kroeg)" do
     #       data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Jason.decode!()
 
-    #       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+    #       {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
     #       assert data["actor"] == "https://puckipedia.com/"
     #     end
@@ -139,7 +184,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     #     test "it works for incoming notices with tag not being an array (kroeg)" do
     #       data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Jason.decode!()
 
-    #       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+    #       {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
     #       assert data["object"]["emoji"] == %{
     #                "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
@@ -147,7 +192,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
 
     #       data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Jason.decode!()
 
-    #       {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+    #       {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
     #       assert "test" in data["object"]["tag"]
     #     end
@@ -170,7 +215,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
         |> Jason.decode!()
         |> Map.put("object", actor.url)
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
       assert data["actor"] == "https://social.tcit.fr/users/tcit"
       assert data["type"] == "Follow"
@@ -289,7 +334,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it works for incoming update activities" do
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
       update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
 
       object =
@@ -302,7 +347,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
         |> Map.put("actor", data["actor"])
         |> Map.put("object", object)
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
+      {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
 
       {:ok, %Actor{} = actor} = Actors.get_actor_by_url(data["actor"])
       assert actor.name == "gargle"
@@ -352,7 +397,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
 
       assert Events.get_comment_from_url(comment_url)
 
-      {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{local: false}, _} = Transmogrifier.handle_incoming(data)
 
       refute Events.get_comment_from_url(comment_url)
     end
@@ -413,14 +458,14 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
         |> Jason.decode!()
         |> Map.put("object", actor.url)
 
-      {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
+      {:ok, %Activity{data: _, local: false}, _} = Transmogrifier.handle_incoming(follow_data)
 
       data =
         File.read!("test/fixtures/mastodon-unfollow-activity.json")
         |> Jason.decode!()
         |> Map.put("object", follow_data)
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
 
       assert data["type"] == "Undo"
       assert data["object"]["type"] == "Follow"
@@ -706,7 +751,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
       actor = insert(:actor)
       other_actor = insert(:actor)
 
-      {:ok, activity} =
+      {:ok, activity, _} =
         MobilizonWeb.API.Comments.create_comment(
           actor.preferred_username,
           "hey, @#{other_actor.preferred_username}, how are ya? #2hu"
@@ -743,7 +788,9 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it adds the json-ld context and the conversation property" do
       actor = insert(:actor)
 
-      {:ok, activity} = MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "hey")
+      {:ok, activity, _} =
+        MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "hey")
+
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert modified["@context"] == Utils.make_json_ld_header()["@context"]
@@ -752,7 +799,9 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
       actor = insert(:actor)
 
-      {:ok, activity} = MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "hey")
+      {:ok, activity, _} =
+        MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "hey")
+
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert modified["object"]["actor"] == modified["object"]["attributedTo"]
@@ -761,7 +810,8 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it strips internal hashtag data" do
       actor = insert(:actor)
 
-      {:ok, activity} = MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "#2hu")
+      {:ok, activity, _} =
+        MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "#2hu")
 
       expected_tag = %{
         "href" => MobilizonWeb.Endpoint.url() <> "/tags/2hu",
@@ -777,7 +827,8 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
     test "it strips internal fields" do
       actor = insert(:actor)
 
-      {:ok, activity} = MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "#2hu")
+      {:ok, activity, _} =
+        MobilizonWeb.API.Comments.create_comment(actor.preferred_username, "#2hu")
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
diff --git a/test/mobilizon_web/resolvers/event_resolver_test.exs b/test/mobilizon_web/resolvers/event_resolver_test.exs
index 3cce2fd09..f1d1e52ba 100644
--- a/test/mobilizon_web/resolvers/event_resolver_test.exs
+++ b/test/mobilizon_web/resolvers/event_resolver_test.exs
@@ -122,6 +122,95 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
              ]
     end
 
+    test "create_event/3 creates an event with an address", %{
+      conn: conn,
+      actor: actor,
+      user: user
+    } do
+      address = insert(:address)
+
+      mutation = """
+          mutation {
+              createEvent(
+                  title: "my event is referenced",
+                  description: "with tags!",
+                  begins_on: "#{
+        DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
+      }",
+                  organizer_actor_id: "#{actor.id}",
+                  category: "birthday",
+                  physical_address: {
+                    street: "#{address.street}",
+                    locality: "#{address.locality}"
+                  }
+              ) {
+                title,
+                uuid,
+                physicalAddress {
+                  url,
+                  geom,
+                  street
+                }
+              }
+            }
+      """
+
+      res =
+        conn
+        |> auth_conn(user)
+        |> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
+
+      assert json_response(res, 200)["errors"] == nil
+
+      assert json_response(res, 200)["data"]["createEvent"]["title"] == "my event is referenced"
+
+      assert json_response(res, 200)["data"]["createEvent"]["physicalAddress"]["street"] ==
+               address.street
+
+      refute json_response(res, 200)["data"]["createEvent"]["physicalAddress"]["url"] ==
+               address.url
+
+      mutation = """
+          mutation {
+              createEvent(
+                  title: "my event is referenced",
+                  description: "with tags!",
+                  begins_on: "#{
+        DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()
+      }",
+                  organizer_actor_id: "#{actor.id}",
+                  category: "birthday",
+                  physical_address: {
+                    url: "#{address.url}"
+                  }
+              ) {
+                title,
+                uuid,
+                physicalAddress {
+                  url,
+                  geom,
+                  street
+                }
+              }
+            }
+      """
+
+      res =
+        conn
+        |> auth_conn(user)
+        |> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
+
+      assert json_response(res, 200)["errors"] == nil
+
+      assert json_response(res, 200)["data"]["createEvent"]["title"] == "my event is referenced"
+
+      assert json_response(res, 200)["data"]["createEvent"]["physicalAddress"]["street"] ==
+               address.street
+
+      assert json_response(res, 200)["data"]["createEvent"]["physicalAddress"]["url"] ==
+               address.url
+    end
+
     test "create_event/3 creates an event with an attached picture", %{
       conn: conn,
       actor: actor,
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 0d417cdda..b8ee13cf4 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -78,6 +78,7 @@ defmodule Mobilizon.Factory do
     %Mobilizon.Addresses.Address{
       description: sequence("MyAddress"),
       geom: %Geo.Point{coordinates: {45.75, 4.85}, srid: 4326},
+      url: "http://mobilizon.test/address/#{Ecto.UUID.generate()}",
       floor: "Myfloor",
       country: "My Country",
       locality: "My Locality",