What you have now
To utilize your methods as above you'll need to go RPC. That is because your example is already half way steeped in the RPC style of doing things. Default WebAPI routes encourage RESTful setups, but if you made a minor alteration to your routes everything would start working. For example you could change your default route to something like a typical MVC route:
routes.MapRoute( name : "Default",
url : "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional });
After adding the route, call things in typical MVC fashion where you use the controller name & action. From your question, however, I suspect you actually want to be RESTful, instead of just getting it to work so read on...
Being RESTful
REST doesn't require HTTP, although the two are often discussed together. REST is really about every resource having a semantically accurate representation. When using HTTP that means unique URI's that respect HTTP semantics. So for example, a call using HTTP GET should never modify data because that violates HTTP's definition of GET and confused HTTP infrastructure like caches.
POST/PUT vs MERGE/PATCH
We're all familiar with GET, POST, PUT, HEAD, etc.. as HTTP methods. Generally, GET is for retrieving, POST is for adding, and PUT is for modifying (although subject to lots of debate). Yet, you have two types of modifications: adding items and removing items from a collection. So are those both PUT or something else? The community hasn't quite settled on how to do this.
Option 1: Custom media type - The HTTP spec really allows for all sorts methods, it's the browsers that really restrict us to the familiar subset. So you can create MERGE (a Roy Fielding work around) or PATCH (an oData work around) methods and define the behavior for this new media type -- maybe one for adding and one for removing.
Option 2: Use POST/PUT - Use PUT for both adding and removing contacts. Just treat the list of ID's like a toggle (if exists remove, if missing add) or alternatley include enough information to know what to do. Then return an HTTP 303 indicating to the client it has a stale state and refresh.
Option 3: Full List - If your set is a reasonable size, you can always pass a complete list of contacts every time you want to update. This way the logic is a super simple wipe and replace.
What really matters from a RESTful perspective is that your application behaves in a consistent way across all methods. So if MERGE means add, it should always mean add. If you expect a complete set of ID's passed to PUT then always pass a complete set.
Controller Design
If it were me, I would break your controller into multiple controllers. One controller deals with Groups another deals Contacts (as a group) and a third deals with one contact within a group. Something like ...
//api/Group/
public List<GroupModel> Get()
public GroupModel Get(int ID)
public GroupModel Post(GroupModel model) //add a group
public GroupModel Put(GroupModel model) //update a group (see comments above)
public void Delete(int ID)
//api/GroupContacts/
public ContactsModel Get() //gets complete list
public void PostContacts(ContactsModel model) //pushes a COMPLETE new state
public void Delete() //delete entire group of contacts
//api/GroupContact/354/
public ContactModel Get(int id) //get contact id #354
public void PostContact(ContactModel model) //add contact (overwrite if exits)
public void Delete(int id) //delete contact if exists
If you want your URL's to appear nested (eg: /api/Group/Contacts
, /api/Group/Contact
), you can look at this other post I wrote. IMHO, asp.net's routing needs a tune up to support nesting a bit easier ...but that's a different issue;-)