diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2017 evuez
+Copyright (c) 2018 evuez
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,10 +2,20 @@
 
 A simple tool to take notes from your terminal (and sync them between your devices).
 
+![`slate status`](https://gist.github.com/evuez/ff11275ea00404472b57520cf92bfed2/raw/423813dd1ce5b6714c5a9d365b9cedb56df66978/slate-status.png)
+
 Generates markdown [task lists](https://help.github.com/articles/about-task-lists/).
 
-Lists are stored in `~/.config/slate/` and their default name is the name of your current directory. You can use any other name you want using the `--name` option.
+**Table of contents**
 
+ - [Install](#install)
+ - [Basic usage](#basic-usage)
+ - [Configuration](#configuration)
+    - [Callbacks](#callbacks)
+      - [sync](#sync)
+      - [status](#status)
+ - [Autocompletion](#autocompletion)
+
 ## Install
 
 ```shell
@@ -75,12 +85,25 @@
 00 - My <b>first</b> note.
 </pre>
 
+Lists are stored in `~/.config/slate/` and their default name is the name of your current directory. You can use any other name you want using the `--name` option.
+
 ## Configuration
 
 The following configuration options can be set in `~/.config/slate/config.toml` (you'll have to create this file).
 
-### sync
+### Callbacks
 
+You must define the commands in this section in a `[callbacks]` table:
+
+```
+[callbacks]
+key1 = value1
+key2 = value2
+...
+```
+
+#### sync
+
 You can use `slate sync` to synchronize your slates. There's no default configuration for this command, so for it to work you'll have to add your own sync command, for example:
 
 ```toml
@@ -89,7 +112,7 @@
 
 This would stage & commit every updates in `~/.config/slate/`, update your local copy and push your updates to the `origin` remote.
 
-### status
+#### status
 
 By default, `slate status` only displays the number of notes by status. You can add a command in the `status` key that'll be used to check if the slate is synchronized or not, for example:
 
@@ -98,3 +121,11 @@
 ```
 
 Where `$SLATE` will be set to `~/.config/slate/<slate name>.md`. The command must return a non-zero exit code if the slate is out of sync and zero if it's synced.
+
+## Autocompletion
+
+You can use the following commands to generate a completion script for your shell:
+
+  - Bash: `slate --bash-completion-script $(which slate)`
+  - Zsh: `slate --zsh-completion-script $(which slate)`
+  - Fish: `slate --fish-completion-script (which slate)`
diff --git a/slate.cabal b/slate.cabal
--- a/slate.cabal
+++ b/slate.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 17c5e9ad0a8ba60142cfd66ace19c7563a5c9c326317bb5a523bdf52094fa452
+-- hash: 6e60912378429734582892684e88ba61f79188de0112612ec1fb3ba8ebffd3d2
 
 name:           slate
-version:        0.8.1.0
+version:        0.9.0.0
 synopsis:       A note taking CLI tool.
 description:    Please see the README on Github at <https://github.com/evuez/slate#readme>
 homepage:       https://github.com/evuez/slate#readme
diff --git a/src/Lib.hs b/src/Lib.hs
--- a/src/Lib.hs
+++ b/src/Lib.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE FlexibleInstances #-}
+
 module Lib
   ( initialize
   , execute
@@ -29,7 +31,7 @@
   , waitForProcess
   )
 import Text.Toml (parseTomlDoc)
-import Text.Toml.Types (Node(VString))
+import Text.Toml.Types (Node(VString), Node(VTable))
 
 type Slate = String
 
@@ -171,23 +173,28 @@
   dir <- getConfigDirectory
   return $ dir ++ s ++ ".md"
 
-getConfigValue :: String -> IO (Maybe String)
-getConfigValue k = do
-  f <- getConfigFile
-  config <- readFile f
-  let Right c = parseTomlDoc "" (fromString config)
-  let vs =
-        case (M.lookup (fromString k) c) of
-          Just (VString s) -> Just (convertString s)
-          _ -> Nothing
-  return vs
+class GetConfig a where
+  getConfigValue :: (String, String) -> IO a
 
-getConfigValueOrFail :: String -> IO String
-getConfigValueOrFail k = do
-  f <- getConfigFile
-  c <- getConfigValue k
-  return $ maybe (error $ "Key `" ++ k ++ "` not found in " ++ f ++ ".") id c
+instance GetConfig (Maybe String) where
+  getConfigValue (s, k) = do
+    f <- getConfigFile
+    config <- readFile f
+    let Right c = parseTomlDoc "" (fromString config)
+    return $
+      case (M.lookup (fromString s) c) of
+        Just (VTable t) ->
+          case (M.lookup (fromString k) t) of
+            Just (VString v) -> Just (convertString v)
+            _ -> Nothing
+        _ -> Nothing
 
+instance GetConfig String where
+  getConfigValue (s, k) = do
+    f <- getConfigFile
+    c <- getConfigValue (s, k)
+    return $ maybe (error $ "Key `" ++ k ++ "` not found in " ++ f ++ ".") id c
+
 displaySlate :: String -> String -> IO ()
 displaySlate s "" = do
   contents <- readFile s
@@ -293,7 +300,7 @@
 
 getSyncStatus :: FilePath -> IO String
 getSyncStatus s = do
-  v <- getConfigValue "status"
+  v <- getConfigValue ("callbacks", "status")
   case v of
     (Just c) -> do
       d <- getConfigDirectory
@@ -310,7 +317,7 @@
 
 syncSlates :: IO ()
 syncSlates = do
-  c <- getConfigValueOrFail "sync"
+  c <- getConfigValue ("callbacks", "sync")
   d <- getConfigDirectory
   (_, _, _, h) <- createProcess (shell c) {cwd = Just d}
   _ <- waitForProcess h
